Search code examples
reactjsreact-nativereact-navigation

non-serializable values were found in the navigation state when passing a function in params


I have two screens:

Screen A

import React, { useState } from "react";
import { Text, View, Button } from "react-native";
const ViewA = ({ navigation }) => {
  const [val, setVal] = useState(null);
  const [val2, setVal2] = useState(null);
  const callBack = (value1,value2) => {
    setVal(value1);
    setVal2(value2);
  };
  const onNextPress = () => {
    navigation.navigate("Second Screen", { callBack: callBack });
  };
  return (
    <View>
      <Text>{val}{val2}</Text>
      <Button title="Next" onPress={onNextPress} />
    </View>
  );
};
export default ViewA;

Screen B

import React from "react";
import { View, Button } from "react-native";

const ViewB = ({ route, navigation }) => {
  const onBackPress = () => {
    const { callBack } = route.params;
    callBack(5,6); // Your new value to set
    navigation.goBack();
  };

  return (
    <View>
      <Button title="back" onPress={onBackPress} />
    </View>
  );
};
export default ViewB;

when I enter screen B a warning appears: non-serializable values were found in the navigation state. how can I solve it?


Solution

  • According to the troubleshooting guide for I get the warning "Non-serializable values were found in the navigation state"

    This can happen if you are passing non-serializable values such as class instances, functions etc. in params. React Navigation warns you in this case because this can break other functionality such state persistence, deep linking etc.

    ...

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

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

    An alternative would be to move the state into route params instead of local state of ViewA (which means you can set it when navigating):

    import React, {useState, useEffect} from 'react';
    import { Text, View, Button } from 'react-native';
    import { NavigationContainer } from '@react-navigation/native';
    import { createStackNavigator } from '@react-navigation/stack';
    
    const Stack = createStackNavigator();
    
    const ViewA = ({ route, navigation }) => {
      const onNextPress = () => {
        navigation.navigate("ViewB", {
          previousScreen: route.name
        });
      };
    
      return (
        <View>
          <Text>ViewA</Text>
          <Text>Params: {JSON.stringify(route.params)}</Text>
          <Button title="Next" onPress={onNextPress} />
        </View>
      );
    };
    
    const ViewB = ({ route, navigation }) => {
      const onBackPress = () => {
        navigation.navigate(route.params.previousScreen, {
          val: 5,
          val2: 6,
        })
      };
    
      return (
        <View>
          <Text>ViewB</Text>
          <Text>Params: {JSON.stringify(route.params)}</Text>
          <Button title="back" onPress={onBackPress} />
        </View>
      );
    };
    
    export default function App() {
      return (
        <NavigationContainer>
          <Stack.Navigator mode="modal">
            <Stack.Screen name="ViewA" component={ViewA} />
            <Stack.Screen name="ViewB" component={ViewB} />
          </Stack.Navigator>
        </NavigationContainer>
      );
    }
    

    Snack