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

React navigation goBack() and update parent state

我有一个页面,如果 s/he 已登录或 "Create an account" 或 "Sign in" 选项将呈现用户名 s/he 未登录。画面如下

他们可以导航到 "Sign in" 或 "Create an account" 页面。成功登录或注册后,它将导航到此页面并显示用户名。画面如下

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

我怎样才能做到这一点?

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

您可以像这样调用导航时将回调函数作为参数传递:

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

然后在login/registration视图中,在返回之前,你可以这样做:

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

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

选项一是带参数导航回parent,在里面定义一个回调函数就可以了,像这样in parent分量:

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

并导航至 child:

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

现在在child中可以调用:

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

或者使用异步函数使其成为 self-update 当它像这样获得新值时。

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

is there a way to pass param from navigate.goback() and parent can listen to the params and update its state?

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

这是一个更清晰的示例,当您从 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 - Refer to https://github.com/react-navigation/react-navigation/issues/288#issuecomment-315684617

致敬

编辑

For 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} />;
  }
}

我只是使用了标准的导航函数,给出了 ViewA 路由名称并传递了参数,完全按照 goBack 会做的。

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

最佳解决方案是使用 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

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

Note* (this is not only for goBack but it will call every-time you enter this page.)

import { NavigationEvents } from 'react-navigation';

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

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

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

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

Each param, route, and navigation state must be fully JSON-serializable for this feature to work. This means that your routes and params must contain no functions, class instances, or recursive data structures.

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

第一屏

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

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

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

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

To achieve this, you can use the navigate method, which acts like goBack if the screen already exists. You can pass the params with navigate to pass the data back

完整示例:

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',
  },
});

根据导航站点,此解决方案为我完成了: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])
);

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

React Navigation 提供了一个钩子,该钩子 return 是一个布尔值,指示屏幕是否聚焦。当屏幕聚焦时钩子将 return 为真,当我们的组件不再聚焦时钩子将为假。

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

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

查看下面的代码或访问: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;

对于我的特定用例,我无法得到任何答案。我有一个从数据库中获取的列表和一个用于添加另一个列表项的屏幕。我希望一旦用户在第二个屏幕上创建了项目,应用程序应该导航回第一个屏幕并在列表中显示新添加的项目。虽然该项目已添加到数据库中,但列表并未更新以反映更改。对我有用的解决方案: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]);

在 v5 中通过 React Navigation 传递回调会引发警告:

This can break usage such as persisting and restoring state

当您从屏幕 B 导航回屏幕 A 时,您可以通过两种简单的方式在屏幕 A 中执行一些代码:

第一个:

useEffect(() => {
    const willFocusSubscription = navigation.addListener("focus", () => handleRefresh());
    return () => willFocusSubscription
}, []);

这就完成了工作。但是,每次渲染屏幕时都会执行此方法。 为了在返回时只渲染一次,您可以执行以下操作:

屏幕 A:

import { DeviceEventEmitter } from "react-native";

useEffect(() => {
    DeviceEventEmitter.addListener("userIsGoingBack", () => handleRefresh());
    return () => DeviceEventEmitter.removeAllListeners("listingCreated");            
}, []);

屏幕 B:

import { DeviceEventEmitter } from "react-native";

DeviceEventEmitter.emit("userIsGoingBack");

如果需要,您还可以将一些数据与发出的事件一起传递以在屏幕 A 中使用。