【问题标题】:React navigation goBack() and update parent state反应导航 goBack() 并更新父状态
【发布时间】:2017-10-28 15:43:36
【问题描述】:

我有一个页面,如果他/她已登录,将呈现用户的姓名,如果他/他未登录,则显示“创建帐户”或“登录”选项。画面如下

他们可以导航到“登录”或“创建帐户”页面。成功登录或注册后,将导航到此页面,并显示用户名。画面如下

目前,我将用户数据存储在AsyncStorage,一旦用户成功登录或从页面重定向时注册,我想更新此字段。

我怎样才能做到这一点?

有没有办法从navigate.goBack() 传递参数,并且父级可以监听参数并更新其状态?

【问题讨论】:

    标签: react-native react-navigation


    【解决方案1】:

    当你像这样调用导航时,你可以传递一个回调函数作为参数:

      const DEMO_TOKEN = await AsyncStorage.getItem('id_token');
      if (DEMO_TOKEN === null) {
        this.props.navigation.navigate('Login', {
          onGoBack: () => this.refresh(),
        });
        return -3;
      } else {
        this.doSomething();
      }
    

    并定义你的回调函数:

    refresh() {
      this.doSomething();
    }
    

    然后在登录/注册视图中,在 goBack 之前,您可以这样做:

    await AsyncStorage.setItem('id_token', myId);
    this.props.navigation.state.params.onGoBack();
    this.props.navigation.goBack();
    

    React Navigation v5 更新:

    await AsyncStorage.setItem('id_token', myId);
    this.props.route.params.onGoBack();
    this.props.navigation.goBack();
    

    【讨论】:

    • 嗨,如果我使用static navigationOptions = ({navigation}) => ({ headerRight: <Button title="test" onPress={() => navigation.navigate('GoToScreen', {onGoBack: () => this.refresh()})} /> });,它会因为没有this.refresh() 而烦恼。有什么解决办法吗?
    • 你也可以用onGoBack: this.refresh代替onGoBack: () => this.refresh()
    • 对于 React Navigation 5.0,使用 this.props.route.params.onGoBack()
    • 在 v5 中,您将收到“我们在导航状态中发现了不可序列化的值,如果您只传递回调函数,这可能会破坏诸如持久化和恢复状态的使用。
    • 官方的选项是“.navigate”回到视图而不是“.goBack”,并传入一个参数。由于路由名称相同,它将弹出当前路由而不是添加新路由。如果父组件是函数组件,您可以执行useEffect(refresh, [route.params?.updateTime]),在子组件中执行.navigate("parent", {updateTime: new Date().getTime()})
    【解决方案2】:

    我也遇到过类似的问题,下面是我通过更详细地解决它的方法。

    选项一是带参数导航回父级,只需在其中定义一个回调函数,像这样在父组件中

    updateData = data => {
      console.log(data);
      alert("come back status: " + data);
      // some other stuff
    };
    

    并导航到孩子:

    onPress = () => {
      this.props.navigation.navigate("ParentScreen", {
        name: "from parent",
        updateData: this.updateData
      });
    };
    

    现在可以在孩子中调用它:

     this.props.navigation.state.params.updateData(status);
     this.props.navigation.goBack();
    

    选项二。为了从任何组件获取数据,正如另一个答案所解释的,AsyncStorage 可以同步使用,也可以不同步使用。

    一旦数据被保存,它就可以在任何地方使用。

    // to get
    AsyncStorage.getItem("@item")
      .then(item => {
        item = JSON.parse(item);
        this.setState({ mystate: item });
      })
      .done();
    // to set
    AsyncStorage.setItem("@item", JSON.stringify(someData));
    

    或者使用异步函数使其在获得新值时自动更新。

    this.state = { item: this.dataUpdate() };
    async function dataUpdate() {
      try {
        let item = await AsyncStorage.getItem("@item");
        return item;
      } catch (e) {
        console.log(e.message);
      }
    }
    

    有关详细信息,请参阅AsyncStorage docs

    【讨论】:

    • 第一种方法对我有用。我的目标是在弹出孩子时引用一些数据。
    • @David 在我的情况下,我将 headerMode 设置为屏幕。这样会自动提供左侧的后退按钮。我不想更改图标。但我想在 goBack() 之后执行一些功能上一屏。请告诉我该怎么做?
    • 另见此示例,方法与选项 1 github.com/react-navigation/react-navigation/issues/… 相同
    • 选项 1 类似于 swift 中的关闭。确实可以,谢谢兄弟
    • 我收到一条警告消息,伙计“[未处理的承诺拒绝:TypeError:未定义不是对象(正在评估'_this.props.navigation.state.params')]”
    【解决方案3】:

    有没有办法从navigate.goback() 传递参数,并且父母可以监听参数并更新其状态?

    您可以将回调函数作为参数传递(如其他答案中所述)。

    这是一个更清晰的示例,当您从 A 导航到 B 并且希望 B 将信息传回 A 时,您可以传递回调(此处为 onSelect):

    ViewA.js

    import React from "react";
    import { Button, Text, View } from "react-native";
    
    class ViewA extends React.Component {
      state = { selected: false };
    
      onSelect = data => {
        this.setState(data);
      };
    
      onPress = () => {
        this.props.navigate("ViewB", { onSelect: this.onSelect });
      };
    
      render() {
        return (
          <View>
            <Text>{this.state.selected ? "Selected" : "Not Selected"}</Text>
            <Button title="Next" onPress={this.onPress} />
          </View>
        );
      }
    }
    

    ViewB.js

    import React from "react";
    import { Button } from "react-native";
    
    class ViewB extends React.Component {
      goBack() {
        const { navigation } = this.props;
        navigation.goBack();
        navigation.state.params.onSelect({ selected: true });
      }
    
      render() {
        return <Button title="back" onPress={this.goBack} />;
      }
    }
    

    debrice 脱帽致敬 - 请参阅 https://github.com/react-navigation/react-navigation/issues/288#issuecomment-315684617


    编辑

    对于 React Navigation v5

    ViewB.js

    import React from "react";
    import { Button } from "react-native";
    
    class ViewB extends React.Component {
      goBack() {
        const { navigation, route } = this.props;
        navigation.goBack();
        route.params.onSelect({ selected: true });
      }
    
      render() {
        return <Button title="back" onPress={this.goBack} />;
      }
    }
    

    【讨论】:

    • 这对于我今天需要跟踪页面上未保存项目的案例非常有帮助。完美运行。
    • 嘿!我可能会迟到,但我收到一个名为 undefined is not an object (evaluating navigation.state.params.onSelect) 的错误不是函数,
    • 感谢您分享 Debrice 的解决方案。它按预期完美运行(:
    • @MinUD 请使用this.props.route.params.onSelect
    • 这很有帮助。谢谢
    【解决方案4】:

    我只是使用了标准的导航函数,给出了 ViewA 的路由名称并传递了参数,这正是 goBack 所做的。

    this.props.navigation.navigate("ViewA", 
    {
         param1: value1, 
         param2: value2
    });
    

    【讨论】:

    • 这与goBack()相同。这会转到前一个视图(它正在添加到堆栈中)。此外,我认为这并不能回答问题。
    • 我认为确实如此 - 问题是“如何使用参数返回”。这与您对 go back vs going forward 的评论无关。在任何一种情况下,参数都可以通过“后退”屏幕传递和接收。
    • @ElliotSchep 如果路由已经在栈上,调用this.props.navigation.navigate和调用goBack()一样。测试一下,你会看到的。屏幕动画从左向右滑动。
    • @Bataleon 我不知道这一点。对不起 morgan_il
    • @ElliotSchep 没问题,这有点令人困惑,而不是我所说的“预期”行为。我还假设navigation.navigate 总是导致将路由推入堆栈。
    【解决方案5】:

    最好的解决方案是使用NavigationEvents。您无需手动创建侦听器。

    强烈建议不要调用回调函数。使用侦听器检查此示例(请记住使用此选项从 componentWillUnMount 中删除所有侦听器)。

    组件 A:

    navigateToComponentB() {
      const { navigation } = this.props
      this.navigationListener = navigation.addListener('willFocus', payload => {
        this.removeNavigationListener()
        const { state } = payload
        const { params } = state
        //update state with the new params
        const { otherParam } = params
        this.setState({ otherParam })
      })
      navigation.push('ComponentB', {
        returnToRoute: navigation.state,
        otherParam: this.state.otherParam
      })
    }
    removeNavigationListener() {
      if (this.navigationListener) {
        this.navigationListener.remove()
        this.navigationListener = null
      }
    }
    componentWillUnmount() {
      this.removeNavigationListener()
    }
    

    组件 B:

    returnToComponentA() {
      const { navigation } = this.props
      const { routeName, key } = navigation.getParam('returnToRoute')
      navigation.navigate({ routeName, key, params: { otherParam: 123 } })
    }
    

    上一个例子的更多细节:https://github.com/react-navigation/react-navigation/issues/288#issuecomment-378412411

    【讨论】:

    • 工作得非常好 - 同意这是迄今为止我遇到的最好的解决方案 IMO
    【解决方案6】:

    不想通过道具管理的小伙伴可以试试这个。每次出现此页面时都会调用它。

    注意*(这不仅适用于 goBack,而且每次您都会调用 进入这个页面。)

    import { NavigationEvents } from 'react-navigation';
    
    render() {
        return (
            <View style={{ flex: 1 }}>
                <NavigationEvents
                  onWillFocus={() => {
                    // Do your things here
                  }}
                />
            </View>
        );
    }
    

    【讨论】:

    • 奇怪,但最好的解决方案。当我们想随时刷新时,它会有所帮助。
    • 这是最好的答案,简单且非常有效,每次返回时我都需要刷新屏幕,无论是导航还是后按,都可以轻松解决。
    【解决方案7】:

    如果您使用redus,您可以创建一个操作来存储数据并检查父级Component 中的值,或者您可以使用AsyncStorage

    但我认为最好只传递JSON-serializable 参数,因为如果有一天你想保存导航状态,这并不容易。

    另请注意react-navigation 在实验中具有此功能 https://reactnavigation.org/docs/en/state-persistence.html

    每个参数、路线和导航状态必须完全 JSON 可序列化以使此功能正常工作。这意味着您的 路由和参数不得包含函数、类实例或 递归数据结构。

    我喜欢开发模式中的这个功能,当我将参数作为函数传递时,我根本无法使用它

    【讨论】:

      【解决方案8】:

      第一屏

      updateData=(data)=>{
          console.log('Selected data',data)
      }   
      
      this.props.navigation.navigate('FirstScreen',{updateData:this.updateData.bind(this)})
      

      第二屏

      // use this method to call FirstScreen method 
      execBack(param) {
          this.props.navigation.state.params.updateData(param);
          this.props.navigation.goBack();
      }
      

      【讨论】:

        【解决方案9】:

        我也会使用navigation.navigate。如果有人遇到同样的问题并且也使用嵌套导航器,它的工作方式如下:

        onPress={() =>
                navigation.navigate('MyStackScreen', {
                  // Passing params to NESTED navigator screen:
                  screen: 'goToScreenA',
                  params: { Data: data.item },
                })
              }
        

        【讨论】:

          【解决方案10】:

          使用 React Navigation v5,只需使用 navigate 方法。来自docs

          为此,您可以使用导航方法,其作用类似于 如果屏幕已经存在,则返回。你可以通过参数 导航以将数据传回

          完整示例:

          import React from 'react';
          import { StyleSheet, Button, Text, View } from 'react-native';
          
          import { NavigationContainer } from '@react-navigation/native';
          import { createStackNavigator } from '@react-navigation/stack';
          
          const Stack = createStackNavigator();
          
          function ScreenA ({ navigation, route }) {
            const { params } = route;
          
            return (
              <View style={styles.container}>
                <Text>Params: {JSON.stringify(params)}</Text>
                <Button title='Go to B' onPress={() => navigation.navigate('B')} />
              </View>
            );
          }
          
          function ScreenB ({ navigation }) {
            return (
              <View style={styles.container}>
                <Button title='Go to A'
                  onPress={() => {
                    navigation.navigate('A', { data: 'Something' })
                  }}
                />
              </View>
            );
          }
          
          export default function App() {
            return (
              <NavigationContainer>
                <Stack.Navigator mode="modal">
                  <Stack.Screen name="A" component={ScreenA} />
                  <Stack.Screen name="B" component={ScreenB} />
                </Stack.Navigator>
              </NavigationContainer>
            );
          }
          
          const styles = StyleSheet.create({
            container: {
              flex: 1,
              backgroundColor: '#fff',
              alignItems: 'center',
              justifyContent: 'center',
            },
          });
          

          【讨论】:

            【解决方案11】:

            这个解决方案为我做了,根据导航网站:https://reactnavigation.org/docs/function-after-focusing-screen/#re-rendering-screen-with-the-useisfocused-hook

            import { useFocusEffect } from '@react-navigation/native';
            
            useFocusEffect(
                React.useCallback(() => { 
                    // YOUR CODE WHEN IT IS FOCUSED
                   return // YOUR CODE WHEN IT'S UNFOCUSED
                }, [userId])
            );
            

            【讨论】:

              【解决方案12】:

              渲染所需组件的最简单方法是使用 useIsFocused 挂钩。

              React Navigation 提供了一个钩子,它返回一个布尔值,指示屏幕是否聚焦。当屏幕聚焦时,钩子将返回 true,当我们的组件不再聚焦时,钩子将返回 false。

              首先将其导入您要返回的所需页面。 import { useIsFocused } from '@react-navigation/native';

              然后,将其存储在任何变量中,并使用 React useEffect hook 渲染组件更改。

              查看以下代码或访问:Here

              import React, { useState } from 'react';
              import { useIsFocused } from '@react-navigation/core';
              
              const HomeScreen = () => {
                const isFocused = useIsFocused();
                
                useEffect(()=>{
                      console.log("Focused: ", isFocused); //called whenever isFocused changes
                  }, [isFocused]);
                  
                  return (
                    <View>
                      <Text> This is home screen! </Text>
                    </View>
                  )
              }
              
              export default HomeScreen;

              【讨论】:

                【解决方案13】:

                我无法为我的特定用例找到任何答案。我有一个从数据库中获取的列表和一个添加另一个列表项的屏幕。我希望一旦用户在第二个屏幕上创建项目,应用程序应该导航回第一个屏幕并在列表中显示新添加的项目。尽管该项目已添加到数据库中,但列表并未更新以反映更改。对我有用的解决方案:https://github.com/react-navigation/react-navigation.github.io/issues/191#issuecomment-641018588

                所以我所做的只是将它放在第一个屏幕上,现在每次屏幕聚焦或失去焦点时都会触发 useEffect。 从“@react-navigation/native”导入 { useIsFocused };

                const isFocused = useIsFocused();
                useEffect(() => {
                    // Code to run everytime the screen is in focus
                }, [isFocused]);
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-11-26
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-06-11
                  • 1970-01-01
                  • 2021-03-31
                  • 2017-09-10
                  相关资源
                  最近更新 更多