19

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?

ksav
  • 20,015
  • 6
  • 46
  • 66
Amin
  • 195
  • 1
  • 1
  • 6

3 Answers3

33

According to the docs 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

ksav
  • 20,015
  • 6
  • 46
  • 66
  • 1
    tanks a lot for your guide. I ignore the warning – Amin Feb 23 '21 at 14:33
  • 1
    FYI `YellowBox` is deprecated as of RN 0.63. Use [`LogBox`](https://reactnative.dev/blog/2020/07/06/version-0.63) instead – biddano Feb 28 '22 at 14:49
  • 2
    ignoring warnings is not the way to handle things, there must be ways of fixing it – nodir.dev Apr 12 '22 at 19:23
  • @nodir.dev Yes there is. See the second half of the answer. – ksav Apr 13 '22 at 02:39
  • Unfortunately, with current versions of React, React Native and React Native Navigation the warning just shifts down one level: params.xxx rather than xxx. They are all properties, – Lorenzo Gatti Apr 19 '22 at 09:14
  • 1
    Saving someone a google - if you use the ignore logbox approach here is the code. `LogBox.ignoreLogs([ 'Non-serializable values were found in the navigation state', ]); ` – Danny Dec 07 '22 at 10:57
0

anyway, if you still need to run that callback from another screen, you can make a custom class to subscribe to events and store the callback like so:

class SetNewPropsListener {
    constructor(){
        this.listeners = []

        this.subscribe = ({ id, cb }) => {
            this.listeners = [...this.listeners.filter((x)=> x.id !== id), { id, cb }]
        }

        this.unsubscribe = ({ id }) => {
            this.listeners = this.listeners.filter((x)=> x.id !== id)
        }

        this.propogate = (id, newProps) => {
            this.listeners.forEach((x)=> x.id === id && x.cb(newProps))
        }
    }
}

export const SetNewProps = new SetNewPropsListener()

and then, in the first screen you're navigating from, you can register callback function with unique id like so:

import { SetNewProps } from '../utils/EventListeners'
const callbackToInvokeFromOtherScreen = (newParamsFromNextScreen)=>{
    // do whatever with new values
}
componentDidMount(){
  SetNewProps.subscribe({ id: 'your_unique_id', cb: callbackToInvokeFromOtherScreen })
}

and then in the next screen you navigate to, you can access the stored callback from the SetNewProps class instance and execute it with your custom params like so:

import { SetNewProps } from '../utils/EventListeners'
const propsToPassToCallback = {}
SetNewProps.propogate('your_unique_id', propsToPassToCallback)

with right logic, this subscriber class method can solve many problems, like invoking peer components methods (like when you have swipe to delete interaction, you don't want any other ListView item to be left open when you swiped another one)

nodir.dev
  • 414
  • 4
  • 5
0

Answer at the top is spot on, unfortunately that is outdated now in react navigation 6. The method now is to pass route.params like this:

//Screen A

const userValues = (value1,value2)


const onNextPress = () => {
    navigation.navigate("ViewB", { userValues(value1,value2): route.params.userValues});
  };

we pass the entire object to the child along with route, essentially allowing the child to receive and pass back.

IVIKOS
  • 11
  • 4