如何在React中使用useEffect来正确处理异步fetch调用?react-hooks/exhaustive-deps

3

你好,我在使用 React 的 useEffect 钩子时遇到了问题。下面的代码可以正常工作,但是 es-lint 建议我在依赖数组中从 useEffect 提供依赖项。

带有// eslint-disable-next-line react-hooks/exhaustive-deps的工作代码

export default function UsersList() {
   const [users, setUsers] = useState<User[]>([]);
   
   const { setError } = useContext(errorContext);
   const { isLoading, setIsLoading } = useContext(globalContext);
   
   useEffect(() => {
       if (users.length < 1) {
         fetchUsers();
       }
       // eslint-disable-next-line react-hooks/exhaustive-deps
     }, []);

     async function fetchUsers () {
       try {
         setIsLoading(true);
         const fetchedUsers = await api.getUsers();
         setUsers(fetchedUsers);
       } catch (error) {
         setError(error);
       } finally {
         setIsLoading(false);
       }
     }
}

无限循环代码

我试图写出这样的代码,但它会触发一个无限循环...(因为函数内部不断地修改状态并因为声明的依赖项而引发 useEffect

 useEffect(() => {
    async function fetchUsers () {
      try {
        setIsLoading(true);
        const fetchedUsers = await api.getUsers();
        setUsers(fetchedUsers);
      } catch (error) {
        setError(error);
      } finally {
        setIsLoading(false);
      }
    }

    if (users.length < 1) {
      fetchUsers();
    }
  }, [setIsLoading, setError, users]);

我还尝试将fetchUsers()放入依赖项数组中,但这没有任何效果。

如何正确设置异步调用以在组件挂载时进行,而无需使用// eslint-disable-next-line react-hooks/exhaustive-deps

1个回答

2
您的 fetchUsers 函数在每次渲染时都会重新创建自身,从而触发 useEffect。您必须通过使用 useCallback 将其包装来保持其引用在渲染之间保持不变,详情请参见 https://reactjs.org/docs/hooks-reference.html#usecallback
此外,为了确保我们仅调用此 useEffect 一次(即在第一次渲染时),我们可以使用 useRef 存储一个布尔值,该值将防止 useEffect 无限循环。
export default function UsersList() {
  const [users, setUsers] = useState<User[]>([]);
  
  const { setError } = useContext(errorContext);
  const { isLoading, setIsLoading } = useContext(globalContext);

  const fetchUsers = useCallback(async function () {
    try {
      setIsLoading(true);
      const fetchedUsers = await api.getUsers();
      setUsers(fetchedUsers);
    } catch (error) {
      setError(error);
    } finally {
      setIsLoading(false);
    }
  }, [setIsLoading, setUsers, setError]);

  // Added a ref here to ensure that we call this function only once in initial render
  // If you need to refetch the users on error, just call fetchUsers
  const isFetchedRef = useRef(false);
  useEffect(() => {
    if (!isFetchedRef.current) {
      isFetchedRef.current = true;
      fetchUsers();
    }
  }, [isLoading, fetchUsers]);
}

谢谢回复,我会在有时间的时候尝试一下!把它写成“工作代码示例”是否不正确?如果是,会有什么不利影响呢? - RMCS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接