多次异步调用后状态更新数据不正确

问题描述 投票:0回答:2

我正在开发一个 React Native 应用程序,并面临一个问题,即在

useCallback
内多次异步函数调用后状态变量设置不正确。具体来说,当我在选项卡之间快速导航时,有时
tipPostList
会填充用于
freePostList
的数据,而
freePostList
保持正确。

CommunityPage.jsx

const [tipPostList, setTipPostList] = useState([]);
const [freePostList, setFreePostList] = useState([]);

useFocusEffect(
  useCallback(() => {
    const fetchPosts = async () => {
      try {
        const [responseTip, responseFree] = await Promise.all([
          getPostsByType("TIP"),
          getPostsByType("FREE"),
        ]);
        setTipPostList(responseTip.data.slice(0, 3));
        setFreePostList(responseFree.data.slice(0, 3));
      } catch (error) {
        Sentry.captureException(error);
      }
    };

    fetchPosts();
  }, []),
);

api.js

// .... axios creation and others...

export const getPostsByType = (type) => {
  return api.get("/posts", {
    params: {
      type,
    },
  });
};

我检查了Postman中的请求,似乎后端返回了一致的结果。 这里似乎有什么问题?我没有编写任何用

tipPostList
设置
freePostList
的代码。

reactjs react-native axios react-state
2个回答
0
投票

确保您的请求仅与当前屏幕相关。 使用标志来检查组件是否仍然已安装,如果检查为 false,则只需避免更新状态即可。

因此,仅当组件仍处于焦点状态时才会发生状态更新,从而防止在选项卡之间快速导航时出现错误更新。

const [tipPostList, setTipPostList] = useState([]);
const [freePostList, setFreePostList] = useState([]);

    useFocusEffect(
  useCallback(() => {
    let isMounted = true; // Define a flag to track if the component is still mounted
    
    const fetchPosts = async () => {
      try {
        const responseTip = await getPostsByType("TIP");
        const responseFree = await getPostsByType("FREE");
        
        if (isMounted) { // Only update state if the component is still mounted
          if (responseTip?.data?.length > 0) setTipPostList(responseTip.data.slice(0, 3));
          
          if (responseFree?.data?.length > 0) setFreePostList(responseFree.data.slice(0, 3));
        }
      } catch (error) {
        console.log(error);
      }
    };

    fetchPosts();

    // Cleanup function to set isMounted to false when unmounting or losing focus
    return () => {
      isMounted = false;
    };
  }, []),
);

0
投票

正如我们所知,异步调用将从主程序中发出,并且仅在完成时才返回。这是为了使调用非阻塞 - 不阻塞主程序。此设置的问题在于,如果多次调用同一异步函数,则每次调用完成后都会返回自己的结果。每次收到结果后,各个州都会得到更新。因此,无法保证状态将获得最新异步调用的结果。很容易不一致。

React 建议以这样的方式进行编码,即状态将获得关于最新异步调用的结果。这是通过忽略所有先前异步调用的结果来实现的。

React-Native 文档中引用的代码片段运行异步效果显示了相同的内容。

isActive 是用于此目的的变量。最重要的一点是清理函数 useEffect 返回。它实际上是一个闭包,可以在其封闭函数中访问完全相同的变量 isActive 。因此,React 运行时可以使用此清理闭包,这会将 isActive 更改为 false,以防当您在标签之间导航时组件在您的情况下卸载。请注意,每个异步调用都由单独的清理函数支持。并且每个异步调用的结果都会被忽略,除了最新的异步调用之外,卸载事件肯定尚未发生。

解决方案:

请为您的代码提供这样的保护,并使状态更新保持一致。

useFocusEffect(
  React.useCallback(() => {
    let isActive = true;

    const fetchUser = async () => {
      try {
        const user = await API.fetch({ userId });

        if (isActive) {
          setUser(user);
        }
      } catch (e) {
        // Handle error
      }
    };

    fetchUser();

    return () => {
      isActive = false;
    };
  }, [userId])
);


 
© www.soinside.com 2019 - 2024. All rights reserved.