当在前台屏幕上更新全局状态时,即使屏幕在后台也会调用 useFocusEffect 挂钩

useFocusEffect hook is invoked even screen is in background when update global state on the screen in foreground

我正在开发 react-native 项目。这个问题主要是关于我项目中的useFocusEffect钩子行为

我有几个屏幕。有一个共享数据&每个屏幕都可以更新数据。因此,我决定使用 context 通过 useState 挂钩来托管数据。当一屏setMyData()时,所有屏都可以看到最新的数据

我的数据上下文通过 useState 挂钩托管共享数据:

import React, {useState} from 'react';

const MyDataContext = React.createContext();

export const MyDataProvider = ({children}) => {
   const [myData, setMyData] = useState([]);
   ...
   return (
    <MyDataContext.Provider
      value={{
        myData,
        setMyData,
      }}>
      {children}
    </MyDataContext.Provider>
  );
}

export default MyDataContext;

(我的 App 组件被 MyDataContext.Provider 包裹着,我只是不在这里显示那部分,因为这不是我的问题的重点)

在我的每个屏幕组件中,当屏幕在前台(可见)时,我需要处理我的数据一次,并用更新后的数据更新全局状态。我使用 useFocusEffect 挂钩。如下所示:

import {useFocusEffect} from '@react-navigation/native';
import MyDataContext from '../context/MyDataContext';

const MyScreenOne = ({navigation})=> {

   const {myData, setMyData} = useContext(MyDataContext);
   
   useFocusEffect(() => {
     console.log('#################  Screen one is FOCUSED! ');
    
     const updatedData = processData([...myData]);
     // set my data
     setMyData(updatedData);
     return () => {
       console.log('Screen one was unfocused');
     };
   }, []);//empty array to force code in hook only run once

   return (<View>
             <MyButton onPress={()=> navigation.navigate("MyScreenTwo")}>
              ...
           </View>
          )
}
...

如您所见,我有一个按钮可以将用户导航到另一个屏幕。另一个屏幕具有与 useFocusEffect 挂钩相同的结构,它在其中处理和更新全局状态数据。

当我 运行 我的应用程序时,以下情况发生在我身上:

  1. 首先MyScreenOne启动。 useFocusEffect 被调用,数据被处理和更新并通过 setMyData()

    设置为全局状态
  2. 由于上一步调用了setMyData(),重新渲染,此时不会再调用MyScreenOneuseFocusEffect,很好,符合预期。

  3. 现在我按下导航到 MyScreenTwo 的按钮。 MyScreenTwo 上发生相同的过程:调用 useFocusEffect,处理和更新数据并通过 setMyData() 设置为全局状态。注意:MyScreenOne 现在不可见并且在背景中 stack/memory。

  4. 由于在上面的步骤中全局状态由第二个屏幕更新,重新渲染再次发生在内存中的所有屏幕上。这次令人惊讶的是 MyScreenOneuseFocusEffect 再次被调用,因为全局状态再次被 MyScreenOne 中的钩子更新,并且重新渲染再次发生在内存中的屏幕上。

  5. 正如您可以想象的那样,由于在上述步骤中意外调用了背景屏幕 MyScreenOneuseFocusEffect,现在会发生无休止的重新渲染。

难道 useFocusEffect 不应该是 运行 只有在屏幕可见时才应该是吗?为什么在重新渲染时也会调用背景屏幕的 useFocusEffect 钩子?如何实现我只需要 运行 在屏幕可见时在每个屏幕上处理数据和更新数据一次,同时数据可以被所有屏幕共享?

useFocusEffect 不采用依赖项数组。您需要按照文档中的说明传递 useCallback

useFocusEffect(
 React.useCallback(() => {
   // whatever
 }, [])
);

https://reactnavigation.org/docs/use-focus-effect/