反应导航goBack()并更新父状态


77

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

在此处输入图片说明

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

在此处输入图片说明

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

我该如何实现?

有没有一种方法可以从中传递参数,navigate.goBack()并且父级可以听这些参数并更新其状态?

Answers:


108

您可以像下面这样调用Navigation时将回调函数作为参数传递:

  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();

1
嗨,如果我使用static navigationOptions = ({navigation}) => ({ headerRight: <Button title="test" onPress={() => navigation.navigate('GoToScreen', {onGoBack: () => this.refresh()})} /> });它,那会很烦this.refresh()。有什么办法吗?
哈利勒·哈拉夫

2
你也可以使用onGoBack: this.refresh ,而不是onGoBack: () => this.refresh()
鸡Suop

1
对于React Navigation 5.0,请使用this.props.route.params.onGoBack()
The1993年

14
在第5版,你会得到“我们发现,在导航状态不可序列值,这可以打破的使用,如保留和恢复状态,如果你只是传递一个回调函数。
托马拉杜-佩特雷斯库

1
官方选项是“ .navigate”而不是“ .goBack”返回视图,并传入参数。由于路由名称相同,因此它将弹出当前路由,而不是添加新路由。如果父级是功能组件,则可以执行useEffect(refresh, [route.params?.updateTime]),而子级可以执行.navigate("parent", {updateTime: new Date().getTime()})
mrvladimir

58

有没有一种方法可以从中传递参数,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} />;
  }
}

整理一下帽子-请参阅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} />;
  }
}

1
对于今天需要记录页面上未保存项目的情况,这非常有用。工作完美。
IronWorkshop

1
嘿! 我可能已经迟到了,但是我收到的错误undefined is not an object (evaluating navigation.state.params.onSelect)不是函数,
Min UD

感谢您分享Debrice的解决方案。它可以按预期正常运行(:
TommyLeong

@MinUD请使用this.props.route.params.onSelect
Ali Akram

我必须添加bind(this)才能this.setState()工作。所以onPress = () => { this.props.navigate("ViewB", { onSelect: this.onSelect.bind(this) }); };
FosAvance

21

对于那些不想通过道具进行管理的人,请尝试此操作。出现此页面时,它将每次调用。

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

import { NavigationEvents } from 'react-navigation';

render() {
    return (
        <View style={{ flex: 1 }}>
            <NavigationEvents
              onWillFocus={() => {
                // Do your things here
              }}
            />
        </View>
    );
}

1
奇怪,但有史以来最好的解决方案。当我们想随时刷新时,它会有所帮助。
Sunil Zalavadiya

1
这是最好的答案,简单又有效,每次导航回到屏幕时,都需要使用导航或后按一次刷新屏幕,这可以毫无问题地解决。
达尼洛

19

我面临着类似的问题,因此这是我通过进一步研究细节来解决的方法。

选项一是使用参数导航回到父级,只需在父级组件中像这样定义回调函数:

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文档


第一种方法对我有用。我的目标是在弹出儿童时查阅一些数据。
Spydy '18年

@David在我的情况下,我将headerMode作为屏幕。这样就自动提供了左侧的后退按钮。我不想更改图标。但是我想在goBack()之后到上一屏幕做一些功能。请告诉我如何做这个?
约翰西

1
还看到该实施例中具有相同的方法,因为选择1 github.com/react-navigation/react-navigation/issues/...
詹弗兰科P.

选项1像快速关闭。谢谢兄弟
克里斯·阮

我收到了警告消息花花公子“ [未处理的承诺被拒绝:TypeError:未定义的不是对象(正在评估'_this.props.navigation.state.params')]“
Mohammed

8

编辑: 最好的解决方案是使用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

问候,尼科尔斯


1
表现出色-同意这是到目前为止我所遇到的最好的IMO解决方案
sbearben

3

我只是使用了标准的导航功能,即提供了ViewA路由名称并传递了参数,正是goBack会做的。

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

5
这是一样的goBack()。这将前进到上一个视图(它正在添加到堆栈中)。而且,我认为这不能回答问题。
Chicken Suop

我认为确实如此-问题是“如何使用参数返回”。不管您对回溯还是前进发表评论。无论哪种情况,参数都可以通过“后退”屏幕传递和接收。
morgan_il

2
@ElliotSchep如果路由已经是在栈上,调用this.props.navigation.navigate 与调用goBack()。测试一下,您将看到。屏幕动画从左到右滑动。
Bataleon

1
@Bataleon我不知道这一点。抱歉morgan_il
Chicken Suop '18

2
@ElliotSchep没问题,这有点令人困惑,不是我所说的“预期”行为。我还假设navigation.navigate总是导致路由被推入堆栈。
Bataleon

0

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

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

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

每个参数,路线和导航状态都必须完全JSON可序列化才能使用此功能。这意味着您的路由和参数不得包含任何函数,类实例或递归数据结构。

我喜欢在开发模式下使用此功能,当我将参数作为函数传递时,我简直无法使用它


0

第一屏

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();
}

0

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

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

0

对于React Navigation v5,只需使用navigation方法。从文档

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

完整示例:

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',
  },
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.