【问题标题】:React Navigation Mounts with Previous Navigation Params, then New ParamsReact Navigation Mounts 与先前的导航参数,然后是新的参数
【发布时间】:2018-05-07 15:36:58
【问题描述】:

我们在 React Native + React Navigation 应用程序中看到了一个奇怪的行为,因此我们创建了一个简单的示例来演示。

我们有 2 个屏幕:A 和 B。

在屏幕 A 上,有两个按钮分别导航到屏幕 B。第一个按钮传递参数 id:1,第二个按钮传递参数 id:2。

在屏幕 B 上,我们在控制台记录了 render() 方法和 componentDidMount() 方法中的入站参数。

如果我单击屏幕 A 上 id:1 的按钮,屏幕 B 上的参数输出会更正,并在 render() 和 componentDidMount() 中记录为 id:1。但是,如果我随后返回并快速单击带有 id:2 的按钮,则记录的输出首先显示为 id:1,然后显示为 id:2。按以下顺序:

首次点击:

渲染:1

componentDidMount: 1

(然后返回,然后快速:)

第二次点击

渲染:1

渲染:2

componentDidMount: 1

componentDidMount: 2

似乎之前安装的组件的幽灵仍然困扰着我们的应用程序。此外,这感觉像是一种竞态条件,因为您必须快速点击才能召唤幽灵,慢速点击不会导致这种行为。

这是整个应用程序:

import React from 'react'
import { View, Text, TouchableOpacity } from 'react-native'
import { StackNavigator } from 'react-navigation'
class ScreenA extends React.Component {
  render() {
    const { navigation } = this.props;
    return (      
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <TouchableOpacity style={{ marginVertical: 12, padding: 32, borderWidth: 1 }} 
          onPress={() => { navigation.navigate( 'ScreenB', { id: 1 })}}>
          <View style={{ }}><Text>Button 1</Text></View>
        </TouchableOpacity>
        <TouchableOpacity style={{ marginVertical: 12, padding: 32, borderWidth: 1 }} 
          onPress={() => { navigation.navigate( 'ScreenB', { id: 2 })}}>
          <View style={{ }}><Text>Button 2</Text></View>
        </TouchableOpacity>
      </View>
    )
  }
}
class ScreenB extends React.Component {
  componentDidMount(){
    console.log('%c componentDidMount: '+this.props.navigation.state.params.id, 'padding: 2px; background: blue; color: #fff')
  }
  render() {
    console.log('%c render: '+this.props.navigation.state.params.id, 'padding: 2px; background: green; color: #fff')
    return (   
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <TouchableOpacity style={{ padding: 16 }} onPress={() => this.props.navigation.goBack()}>
          <View style={{ }}><Text>Button</Text></View>
        </TouchableOpacity>        
      </View>
    )
  }
}
const Router = StackNavigator({
  ScreenA: { screen: ScreenA },
  ScreenB: { screen: ScreenB }
})
export default class App extends React.Component {
  render() {
    return (
      <Router />
    )
  }
}

这里还有一个小吃:

https://snack.expo.io/@makerepeat/navigation-bugs

... 和 package.json:

{
  "name": "rn2-test2",
  "version": "0.1.0",
  "private": true,
  "devDependencies": {
    "react-native-scripts": "1.14.0",
    "jest-expo": "~27.0.0",
    "react-test-renderer": "16.3.1"
  },
  "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
  "scripts": {
    "start": "react-native-scripts start",
    "eject": "react-native-scripts eject",
    "android": "react-native-scripts android",
    "ios": "react-native-scripts ios",
    "test": "jest"
  },
  "jest": {
    "preset": "jest-expo"
  },
  "dependencies": {
    "expo": "^27.0.1",
    "react": "16.3.1",
    "react-native": "~0.55.2",
    "react-navigation": "^1.5.12"
  }
}

【问题讨论】:

    标签: javascript reactjs react-native react-navigation


    【解决方案1】:

    这里的问题是navigation.navigate 是如何工作的。如果您在没有传递下一个屏幕的密钥的情况下使用它,react-navigation 将不会与已安装的屏幕匹配,因此会安装另一个屏幕。

    您可以在此处观察到的情况正是组件尚未卸载但导航不知道并呈现同一组件的另一个实例时的情况。

    您可以通过将密钥参数传递给navigate 函数来防止这种情况,如下所示:

    navigation.navigate({routeName: 'ScreenB', key: 'ScreenB', params: { id: 1}})
    
    navigation.navigate({routeName: 'ScreenB', key: 'ScreenB', params: { id: 2}})
    

    现在StackNavigator 可以检查是否已经存在具有相同键的屏幕并阻止它安装。

    【讨论】:

    • 我们完全误解了密钥的用途。非常感谢你,你太棒了。
    猜你喜欢
    • 1970-01-01
    • 2018-03-28
    • 2022-11-11
    • 2020-03-27
    • 1970-01-01
    • 2020-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多