在 useEffect 中处理 forEach 后调用 setstate

Calling setstate after processing forEach within useEffect

我试图在 useEffect 上处理一些 api 数据,在处理数据时我需要将其设置为各种状态,在处理数据时,由于某些原因 useEffect 被无限触发。虽然从 class 组件我知道 setState 是一个异步操作,它不等待 forEach 的完成。我们有功能组件的解决方法吗?

  useEffect(() => {
    const apiResponse = fetchAppName();  
    apiResponse.then(
        (response) => {
          // if (response.statusCode === '200' && response.status.statusType === 'SUCCESS' && response.data) {
            const dataFields = {};
            const appNameValues = [];
            dummyData.data.forEach((ele) => {
              const workFlowIds = [];
              ele.workflowConfigs.forEach((wc) => {
                workFlowIds.push(wc.workflowId);
              })
              dataFields[ele.name] = workFlowIds;
            });
            Object.keys(dataFields).forEach((k) => {
              appNameValues.push(k);
            });
            setAppData(dataFields);
            setDropDownOptions({
              ...dropDownOptions,
              appName: appNameValues,
            });
          // }
      }).catch((e)=>{
        console.log('Failed to fetchAppNames due to: ', e);
      });
  },[setDropDownOptions, dropDownOptions]);

问题

不要使用由效果更新的依赖项。换句话说,dropDownOptions 是一个依赖项,效果会更新它,从而创建一个渲染循环。

解决方案

state updater 函数是稳定的,所以它是不必要的,并且使用一个功能性的 state update 来移除另一个 dropDownOptions 依赖。

useState

Note

React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.

useEffect(() => {
  const apiResponse = fetchAppName();  
  apiResponse.then(
    (response) => {
      // if (response.statusCode === '200' && response.status.statusType === 'SUCCESS' && response.data) {
        const dataFields = {};
        const appNameValues = [];
        dummyData.data.forEach((ele) => {
          const workFlowIds = [];
          ele.workflowConfigs.forEach((wc) => {
            workFlowIds.push(wc.workflowId);
          })
          dataFields[ele.name] = workFlowIds;
        });
        Object.keys(dataFields).forEach((k) => {
          appNameValues.push(k);
        });
        setAppData(dataFields);
        setDropDownOptions(dropDownOptions => ({
          ...dropDownOptions,
          appName: appNameValues,
        }));
      // }
    }).catch((e)=>{
      console.log('Failed to fetchAppNames due to: ', e);
    });
},[]); // will run on mount, add other dependency triggers if needed.