【问题标题】:Chaining useEffects with early return链接 useEffects 并提前返回
【发布时间】:2021-08-27 03:52:09
【问题描述】:

使上下文清晰的示例:

我正在尝试使用来自 API 调用的两组数据来呈现组件。如果第一个 API 调用失败,我也会提前返回。第二个 API 调用依赖于第一个 API 结果的数据。我不想结合这两种效果,因为这意味着整个组件在我获得机器人 API 结果之前不会呈现。

这是伪代码

const DataList = () => {
  const [dataFromEffect1, setDataFromEffect1] = useState([]);
  const [dataFromEffect2, setDataFromEffect2] = useState([]);

  useEffect(() => {
    const callApi1 = async () => setDataFromEffect1(await (await fetch('/api1')).json());
    callApi1();
  }, []);

  // early return so that all the complex logic below is not called on ever render
  if (!dataFromEffect1) return <div>No Data1</div>;

  const data1 = complexMassagingOver(dataFromEffect1); // data1 to be used in second effect

  useEffect(() => {
    const callApi2 = async () => setDataFromEffect2(await (await fetch('/api2', { headers: data1 })).json());
    callApi2();
  }, [data1]);

  return (
    <div>
      {/* no need to null check here, because of the early return on top */}
      {dataFromEffect1}
      {/* null check required here, so that it doesnt render this child component to not render till we get the data for it */}
      {dataFromEffect2 ? (
        <div>
          {dataFromEffect2}
        </div>
      ) : null}
    </div>
  );
};

问题

上面的代码不起作用,因为你不能有条件地添加一个 useEffect (提前返回搞砸了) 试图找到解决此问题的最佳解决方法。

【问题讨论】:

    标签: reactjs react-hooks use-effect early-return


    【解决方案1】:

    只需为dataFromEffect2 创建一个组件:

    const DataList = () => {
      const [dataFromEffect1, setDataFromEffect1] = useState([]);
      const [dataFromEffect2, setDataFromEffect2] = useState([]);
    
      useEffect(() => {
        const callApi1 = async () =>
          setDataFromEffect1(await (await fetch("/api1")).json());
        callApi1();
      }, []);
      
      if (!dataFromEffect1) return <div>No Data1</div>;
    
      const data1 = complexMassagingOver(dataFromEffect1); // data1 to be used in second effect
    
      return (
        <div>
          {dataFromEffect1}
          <Component data1={data1} dataFromEffect2={dataFromEffect2} setDataFromEffect2={setDataFromEffect2} />
        </div>
      );
    };
    
    const Component = ({ data1, dataFromEffect2, setDataFromEffect2 }) => {
      useEffect(() => {
        const callApi2 = async () =>
          setDataFromEffect2(
            await (await fetch("/api2", { headers: data1 })).json()
          );
        callApi2();
      }, [data1]);
    
      return <div>{dataFromEffect2}</div>;
    };
    

    【讨论】:

      【解决方案2】:

      你可以试试这样的。它将杀死两个 useEffects,但它不会运行第二个,除非它从第一个检索数据。此外,我没有在您的代码中解决此问题,但您不应在 useEffect 中使用 async 代码。这可能导致内存泄漏和意外错误。您也没有清理来自useEffect 的提取。 Academind 有一个很好的博客解释了如何解决这个问题以及如果你保留这样的代码会发生什么https://academind.com/tutorials/useeffect-abort-http-requests/

      const DataList = () => {
        const [dataFromEffect1, setDataFromEffect1] = useState([]);
        const [dataFromEffect2, setDataFromEffect2] = useState([]);
      
        useEffect(() => {
          const callApi1 = async () => setDataFromEffect1(await (await fetch('/api1')).json());
          callApi1();
        }, []);
      
       useEffect(() => {
          if(!dataFromEffect1.length) return;
          const data1 = complexMassagingOver(dataFromEffect1); // data1 to be used in second effect
          const callApi2 = async () => setDataFromEffect2(await (await fetch('/api2', { headers: data1 })).json());
          callApi2();
        }, [dataFromEffect1]);
      
        // early return so that all the complex logic below is not called on ever render
        if (!dataFromEffect1) return <div>No Data1</div>;
      
      
        return (
          <div>
            {/* no need to null check here, because of the early return on top */}
            {dataFromEffect1}
            {/* null check required here, so that it doesnt render this child component to not render till we get the data for it */}
            {dataFromEffect2 ? (
              <div>
                {dataFromEffect2}
              </div>
            ) : null}
          </div>
        );
      };
      

      【讨论】:

        【解决方案3】:

        两个选项(我现在选择第二个):

        1. 按照@viet 在answer below 中的建议,创建一个单独的组件来抽象第二个 useEffect。
        2. 将 useEffect 移到提前返回的上方,但我必须在效果内复制 if 条件。如answer below 中所述。

        【讨论】:

          猜你喜欢
          • 2012-07-28
          • 2021-09-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-19
          相关资源
          最近更新 更多