How to pass parent function to child screen in React Navigation 5?
Asked Answered
B

3

16

Recently I have started using React Native 0.61.5 and React navigation 5.x. Now I need to pass a parent function into a child screen. For example, I want to change navbar icon by pressing button in tab of TabNavigator nested in StackNavigator. Previously (navigation ver. 3.x) I was using getActiveChildNavigationOptions for my needs but now it's removed. So the only way to achieve this goal is to pass custom functions to child components.

Here in example code I want to change from XXXXXXXX to YYYYYYYYY:

const TabA = ({ route }) => (
  <View>
    <Text>Tab A</Text>
      <Button 
        title="Change header"
        onPress={() => route.params.setHeaderRightName("YYYYYYYYY")}/>
  </View>
)

const TabB = () => <><Text>Tab B</Text></>

const Tabs = createBottomTabNavigator();
const TabsNavigator = ({ navigation }) => {
  const [headerRightName, setHeaderRightName] = useState("XXXXXXXX");

  useLayoutEffect(() => navigation.setOptions({
    headerRight: () => <MyNavIcon name={headerRightName}/>
  }), [headerRightName]);

  return (
    <Tabs.Navigator>
      <Tabs.Screen name="tab-a" component={TabA} initialParams={{ setHeaderRightName }} />
      <Tabs.Screen name="tab-b" component={TabB} />
    </Tabs.Navigator>
  );
}

const Stack = createStackNavigator();
export default App = () => (
  <NavigationContainer>
    <Stack.Navigator>
      <Stack.Screen name="tabs" component={TabsNavigator} />
      <Stack.Screen name="another-screen" component={AnotherScreen} />
    </Stack.Navigator>
  </NavigationContainer>
)

Everything is working fine, but I am getting this warning:

enter image description here

Full text:

Non-serializable values were found in the navigation state, which can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/troubleshooting#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state for more details.

So how can I pass function from parent to child if warning says I cannot pass function using route params?

Belemnite answered 31/3, 2020 at 15:56 Comment(0)
B
19

The documentation page the link in the warning is pointing to says:

If you don't use state persistence or deep link to the screen which accepts functions in params, then you can ignore the warning. To ignore it, you can use YellowBox.ignoreWarnings.

So, in my case, I can ignore this warning and pass functions thought route params safely.

YellowBox.ignoreWarnings([
  'Non-serializable values were found in the navigation state',
]);

or for react native 0.63 and higher:

LogBox.ignoreLogs([
  'Non-serializable values were found in the navigation state',
]);
Belemnite answered 1/4, 2020 at 10:4 Comment(1)
@bmovement I hate solutions like this too. But even contributors made me sure this is the correct way.Belemnite
L
7

YellowBox is deprecated in react-native > 0.6. Use the following code and also remember this is not the main error. this is only a warning

import { LogBox } from 'react-native';
LogBox.ignoreLogs([
  'Non-serializable values were found in the navigation state',
]);

Oh. know its gone. Thank you

Note: And the above question is passed the setState const variable to the screen via initialParams. So we can't pass the functions or any other executable modules via initialParams and this is only pass the string type of data. So please use the following code for pass the functions, setvariables, etc...

const [HeaderRightName,setHeaderRightName] = React.useState('');
<Tabs.Screen name="tab-a" options={{ headerShown: false }} >
           {(props) => (
                <TabA setHeaderRightName={setHeaderRightName} {...props}/>
           )}
</Tabs.Screen
Lockout answered 26/9, 2020 at 13:29 Comment(0)
D
2
class MainScreen extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      title: null
    };

    this.updateTitle = this.updateTitle.bind(this);
  }

  updateTitle(title) {
    this.setState({title: title});
  }

  render() {
    return (
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen 
          name="NewScreen"
          options={{
            headerShown: true,
            headerTitle: this.state.title
          }}>
            {(props) => <NewScreen  {...props} updateTitle={this.updateTitle}/>}
          </Stack.Screen>
        </Stack.Navigator>
      </NavigationContainer>
    );
  }
}
Dowson answered 16/12, 2020 at 12:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.