React (Native) Context API 导致 Stack Navigator (React Navigation 5) 在状态更新后重新渲染

Posted

技术标签:

【中文标题】React (Native) Context API 导致 Stack Navigator (React Navigation 5) 在状态更新后重新渲染【英文标题】:React (Native) Context API Causes Stack Navigator (React Navigation 5) to re-render after state update 【发布时间】:2020-11-21 14:05:01 【问题描述】:

提前感谢您帮助解决这个问题。


一些上下文(没有双关语)

我正在从事一家公司的项目,该项目将限制我将所有代码放在这里,但我会尝试在不泄露国家机密的情况下尽可能多地放上相关代码。

基本上,React Native 应用程序使用的是 React Navigation v5,我已经尝试解决这个问题两天了。我明白发生了什么,但我不明白为什么会发生或如何解决它。

在应用程序代码的某处,您有以下代码。

export const Lounge = function Lounge(props: navigation: any) 
  
  const navigation: setOptions = props
  const bottomTabBarCtx = useContext(BottomTabBarContext)

  // const [routeName, setRouteName] = useState("LoungeList")

  useEffect(() => 
    setOptions(tabBarVisible: bottomTabBarCtx.isVisible)
  , [bottomTabBarCtx.isVisible, setOptions])

  return <Stack.Navigator initialRouteName="LoungeList">
    <Stack.Screen
      name="LoungeList"
      component=List
      options=headerShown: false
    />

    <Stack.Screen
      name="LoungeDetails"
      component=Details
      options=
        headerLeft : buttonProps => <BackButton ...buttonProps />,
        headerRight: buttonProps => <LoungeHeaderRightDetailsButton ...buttonProps />,
        headerTitle: 'Lounge Details',
      
    />

    <Stack.Screen
      name="LoungeParticipants"
      component=Participants

      options=
        headerLeft : buttonProps => <BackButton ...buttonProps />,
        headerRight: buttonProps => <LoungeHeaderRightDetailsButton ...buttonProps />,
        headerTitle: 'Lounge Participants',
      
    />

    <Stack.Screen
      name="LoungeAdd"
      component=LoungeEdit

      options=
        headerLeft : buttonProps => <BackButton ...buttonProps />,
        headerTitle: 'Create Lounge',
      
    />

    <Stack.Screen
      name="LoungeEdit"
      component=LoungeEdit

      options=
        headerLeft : buttonProps => <BackButton ...buttonProps />,
        headerTitle: 'Edit Lounge',
      
    />

...

上面的文件只是定义了可以在应用程序的这个部分导航到的路线。

初始屏幕/路线名称 = 'LoungeList'

一切正常。

LoungeList 屏幕上,我点击了一个休息室摘要单元格(请发挥你的想象力),然后点击将我带到下面的屏幕:LoungeDetails

LoungeDetails 屏幕中,我看到了休息室的详细信息,并且在该屏幕上,还有一个 加入按钮。这个按钮可以让我加入休息室。

功能应该是,当用户点击加入按钮时,按钮进入加载状态,当它完成加载时,它应该显示为Joined。发生这种情况应该但是在发生这种情况后立即我被导航到 LoungeList 屏幕

以下是我为加入休息室而执行的代码:

const userDidJoinLounge = (joinedLounge: LoungeWithUsers) => 

    if(joinedLounge)

      console.log("joinedLounge before update == ", joinedLounge);
      
      const allLoungesNew = LoungeController.replaceLoungeInLoungeArray(
        userCtx.allLoungesList,
        joinedLounge
      );

      const userLoungesNew = LoungeController.getUserJoinedLoungeArrayListOnJoin(
        allLoungesNew,
        userCtx.userLoungeList,
        joinedLounge
      );

      console.log("allLounges after update == ", allLoungesNew);
      console.log("joinedLounges after update == ", userLoungesNew);

      userCtx.updateLoungeLists(allLoungesNew, userLoungesNew)

    
  

在上面的函数中,一行

userCtx.updateLoungeLists(allLoungesNew, userLoungesNew) 接受两个参数,一个应用程序上所有休息室的列表,一个用户加入的休息室列表。此函数更新包含这两个值的上下文。


大问题

问题我不断被重定向到 LoungeList 屏幕,即使没有任何代码指示应用这样做

可能有帮助的事情

我玩过很多事情,其中​​一些可能有助于您弄清楚发生了什么的事情是:

React Navigation 路由配置似乎正在重新渲染。我这样说是因为当我将 initialRouteName 更改为 LoungeDetails 时,休息室详情屏幕(加入按钮所在的位置)会闪烁,但它会停留在同一屏幕上。这也告诉我,在该函数结束时,堆栈导航器会退回到 initialRouteName 是什么。

奇怪的是,当我只设置 allLoungesNew 时,应用程序不会导航到 initialRouteName,似乎只有当我在 userContext 的状态下设置 userLoungesNew 时才会出现此问题。

当用户点击加入按钮时,如何阻止 react-navigation 离开屏幕?

非常感谢您的帮助。我希望我已经为您提供了足够的信息来解决问题。请随时提出更多问题以帮助解决问题。我会告诉你我可以做什么。

【问题讨论】:

【参考方案1】:

在网上搜索了大约 3 天的答案后,答案很简单。

我发现许多文章和建议告诉我不要将任何用于创建导航器的代码放入组件本身。

我没有为这个应用程序编写原始代码,我是在大约 2 个月前开始的,虽然我查看了嵌套导航器,但我并没有立即想到问题可能位于导航链/堆栈的更高位置。

在根index.tsx 文件中,以前的开发人员将根导航器(包含所有其他子/嵌套导航器的导航器)放在入口点组件中。

这个文件是在组件中创建任何导航器的唯一位置,但我没想到那里查看,因为行为似乎局限于应用程序的休息室部分

解决方案

无论如何,修复很简单。在根 index.tsx 文件中,我将每个导航器声明移出(根)功能组件,从而解决了问题。

很明显,每当我对我的 UserContext 中保存的状态调用状态更改时,导航器就会被取消/重新挂载,现在这是有道理的,因为创建导航器的代码在组件内部,每当我更改状态,导航器也会被重新渲染,这让我回到了initialRouteName 屏幕。

主要经验教训

React Navigation v5 与其早期版本不同,现在允许我们使用 JSX 标签声明导航器,这通过为导航器的动态配置等提供机会,为我们提供了更大的灵活性,但是我们必须小心 在哪里有任何/我们所有的导航器都被声明了

在解决这个问题 3 天后,一切都指向了这个解决方案,但我认为我正在处理的代码中并非如此,因为除了 一个文件,所有其他导航器声明是在组件外部进行的。

对你来说可能是一样的。 如果您在状态更改时遇到这种行为,那么问题导航器可能会在您的导航器链中向上

我希望这对某人有所帮助! ?

【讨论】:

以上是关于React (Native) Context API 导致 Stack Navigator (React Navigation 5) 在状态更新后重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

在 React Native 中使用 Context 和 React Navigation 处理嵌套屏幕上的更新

React (Native) Context API 导致 Stack Navigator (React Navigation 5) 在状态更新后重新渲染

React Native Context 不会跨文件和屏幕更新

React Native 状态管理(Redux、Context API 或 Graphql)

React Native Context,如何在多个嵌套文件和组件之间共享上下文

React Native with Context API 警告:“允许需要循环,但可能导致未初始化的值......”