如何更新 useEffect 钩子中异步函数内的数组?

时间:2021-06-16 11:05:16

标签: reactjs asynchronous

我正在开发一个连接到 firebase 数据库的 React 应用程序。应用程序的一部分读入属于当前用户的 Items 列表,然后为每个读入一个 config 对象,最后更新状态中的项。我在让它工作时遇到问题 - 我似乎一直在工作

我的组件是:

const Dashboard = () => {

  const authUser = useContext(AuthUserContext);
  const firebase = useContext(FirebaseContext);

  const [error, setError]                   = useState<string|null>(null);
  const [firebaseToken, setFirebaseToken]   = useState<string|null>(null);
  const [items, setItems]                   = useState<Array<ItemModel>>([]);

  // On first render, get all Items
  useEffect(() => {

    if(!authUser) return;
    if(!firebase) return;
    
    let userId = authUser.id;

    const getItems = () => {

      firebase.doGetIdToken()
      .then((token) => {

        // Save token so it can be passed down
        setFirebaseToken(token);

        url = "items/" + userId;
        Client.getData(url, token)
        .then((itemResults:Array<ItemModel>) => {

            // Get config for each Item
            // Set up an empty array to hold the new data
            const itemResultsWithConfigs:Array<ItemModel> = []
            // Now get the config for each item
            itemResults.forEach((item:ItemModel) => {

                // Get config for this Item
                url = "/items/config/" + item.id;
                Client.getData(url, token)
                .then((newConfig:ConfigModel) => {

                  let newItem:ItemModel = {
                    ...item, 
                    config:  newConfig
                  }

                  // Add full item to list & update list
                  itemResultsWithConfigs.push(newItem);

                })

              })
              
              setItems(itemResultsWithConfigs);

            })

          });

        })
      })
      .catch(() => setError("Unable to connect to database"))
    }

    getItems();

  }, [authUser, firebase])

  return (
    <>
      <ul>
        {
          items.map((item:ItemModel) => {
              return <li key={item.id}>{item.name}</li>
          })
        }
      </ul>
    </>
  );
}

export default Dashboard;

Client.getData 是:

async function getData(path:string, token:string) {

  const object:AxiosRequestConfig = {
    ...obj,
    method: 'GET',
    headers: {
      ...obj.headers,
      'Authorization': `Bearer ${token}`,
    },
  };

  try {
    const response:AxiosResponse      = await axios.get(`${baseUrl}${path}`, object);
    checkStatus(response);
    const parsedResult  = parseJSON(response);
    return parsedResult;

  } catch (error) {
    throw error;
  }
    
}

问题是异步函数 (getData) 在不同时间返回,因此有时会覆盖项目数组。目前这只是渲染一两个项目,而不是我知道应该在那里的 3 个。

我该如何处理?

1 个答案:

答案 0 :(得分:2)

由于 itemResultsWithConfig 是异步派生的,一个好主意是使用 Promise.all 映射并等待所有承诺解析

const getItems = () => {

  firebase.doGetIdToken()
  .then((token) => {

    // Save token so it can be passed down
    setFirebaseToken(token);

    url = "items/" + userId;
    Client.getData(url, token)
    .then((itemResults:Array<ItemModel>) => {

        // Get config for each Item
        // Set up an empty array to hold the new data
        // Now get the config for each item
        let promises = itemResults.map((item:ItemModel) => {

            // Get config for this Item
            url = "/items/config/" + item.id;
            return Client.getData(url, token)
            .then((newConfig:ConfigModel) => {

              let newItem:ItemModel = {
                ...item, 
                config:  newConfig
              }

              return newItem;

            })

          })
          Promise.all(promises).then((itemResultsWithConfigs:Array<ItemModel>) => setItems(itemResultsWithConfigs))
          

        })

      });

    })