React Hooks异步处理:Promise与async/await在Hook中的应用

编程艺术家 +0/-0 0 0 正常 2025-12-24T07:01:19 React Hooks · Async/Await · promises

在React Hooks中处理异步操作是每个开发者都会遇到的挑战。最近在项目中踩了一个关于Promise和async/await在自定义Hook中使用的大坑,分享给大家。

问题场景

我需要创建一个获取用户信息的自定义Hook,最初写法如下:

const useUserData = (userId) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!userId) return;
    
    // 错误写法:直接返回Promise
    fetch(`/api/users/${userId}`)
      .then(response => response.json())
      .then(setData)
      .catch(setError);
  }, [userId]);

  return { data, loading, error };
};

踩坑经历

这个看似正常的代码在实际使用中出现了严重问题:当用户快速切换时,会出现竞态条件(race condition),导致数据错乱。比如用户从ID=1切换到ID=2,但ID=1的请求后返回,会覆盖掉ID=2已经获取到的数据。

正确解决方案

通过查阅资料和踩坑总结,正确的做法应该是:

const useUserData = (userId) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!userId) return;
    
    // 使用AbortController处理取消请求
    const abortController = new AbortController();
    
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`, {
          signal: abortController.signal
        });
        const result = await response.json();
        
        // 只有在请求未被取消时才更新状态
        if (!abortController.signal.aborted) {
          setData(result);
        }
      } catch (err) {
        if (!abortController.signal.aborted) {
          setError(err);
        }
      } finally {
        if (!abortController.signal.aborted) {
          setLoading(false);
        }
      }
    };

    fetchData();
    
    // 清理函数:组件卸载时取消请求
    return () => {
      abortController.abort();
    };
  }, [userId]);

  return { data, loading, error };
};

关键要点

  1. 使用async/await替代Promise链,代码更清晰
  2. 引入AbortController处理请求取消
  3. 在清理函数中正确abort请求
  4. 添加条件判断避免竞态条件

这个坑踩得有点痛,但收获满满。大家在使用自定义Hook处理异步时一定要注意这些细节!

推广
广告位招租

讨论

0/2000