React - 带有异步 setState 的 useEffect 导致额外的重新渲染

Posted

技术标签:

【中文标题】React - 带有异步 setState 的 useEffect 导致额外的重新渲染【英文标题】:React - useEffect with async setState causing additional re-renders 【发布时间】:2020-11-08 16:20:32 【问题描述】:

我这里有一个 React 钩子

export const useConfigDetails = (resourceName: string) => 
  const [isLoading, setLoading] = React.useState<boolean>(true);
  const [error, setError] = React.useState<AxiosError>(undefined);
  const [allEnvDetails, setAllEnvDetails] = React.useState<ResourceDetails[]>();


  React.useEffect(() => 
   const configPromises = ['dev', 'stage', 'prod'].map((environment: Environment) =>
    axios.get('someUrlGetEndpoint'));

    Promise.all(configPromises)
      .then((resp: ResourceDetails[]) => setAllEnvDetails(resp))
      .catch((err: AxiosError) => setError(err))
      .finally(() => setLoading(false));
  , [resourceName]);

  return  allEnvDetails, isLoading ;
;

我想要实现的目标 - 每当 resourceName 更改时,它需要刷新并调用所有不同的环境 (dev, stage and prod),并返回最新的 allEnvDetails。它应该避免其他重新渲染

理想情况下,应该只有 3 个 axios.get 调用对应于 3 个环境。但是,它被调用了 9 次(每个环境 3 次)。

根据this SO question,我在promise resolve/reject 块中使用setState,所以每当功能组件(我的钩子)中的状态更新时,它都会触发另一个不需要的重新渲染。

有没有更好的方法来重构/解决这个问题?我一直在寻找一段时间,但我不确定有什么可以改进的。

【问题讨论】:

您有三个初始状态(3 个渲染),由于React.strictMode 和组件安装后的重新渲染增加了一倍,所以它变成了 9,这里回答 ***.com/questions/62258852/…跨度> 【参考方案1】:

您可以尝试在函数开头添加条件 if(isLoading):

`
React.useEffect(() => 
if(isLoading)
const configPromises = ['dev', 'stage', 'prod'].map((environment: 
Environment) =>
axios.get('someUrlGetEndpoint'));

Promise.all(configPromises)
  .then((resp: ResourceDetails[]) => setAllEnvDetails(resp))
  .catch((err: AxiosError) => setError(err))
  .finally(() => setLoading(false));

, [resourceName]);

`

【讨论】:

【参考方案2】:

试试这个代码:

export const useConfigDetails = (resourceName: string) => 
  const [isLoading, setLoading] = React.useState<boolean>(true);
  const [error, setError] = React.useState<AxiosError>(undefined);
  const [allEnvDetails, setAllEnvDetails] = React.useState<ResourceDetails[]>();


  React.useEffect(() => 
      (async () => 
          try
              setLoading(true)

              const dev = await axios.get('someUrlGetEndpoint');
              const stage = await axios.get('someUrlGetEndpoint');
              const prod = await axios.get('someUrlGetEndpoint');

              setAllEnvDetails([
                  dev,
                  stage,
                  prod
              ])

              setLoading(false)
          catch(e) 
              setError(e);
              setLoading(false)
          
      )()
  , [resourceName]);

  return  allEnvDetails, isLoading ;
;

【讨论】:

以上是关于React - 带有异步 setState 的 useEffect 导致额外的重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

react中的setState到底是同步还是异步?

react中的useState与setState的异步问题

React--setState同步/异步问题

面试题:React中setState是异步还是同步?

react 异步的setState

react的setState函数同步还是异步?