Search code examples
reactjsreact-nativereact-hooksreact-navigationreact-navigation-v5

React hook state variable sharing between React Navigation stack screens?


EDIT: For those stumbling on this, disregard this question. It was a simple mistake on my end. React hook variable sharing between screen components works fine.

I have my App.js file as follows

import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import Main from './src/Main';
import Game from './src/Game';
const Stack = createStackNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Main" component={Main} />
        <Stack.Screen name="Game" component={Game} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

Main.js (relevant parts)

export default function Main({ navigation }) {
  const [username, setUsername] = useState(null);
  ...
  <TextInput
    mode='outlined'
    label='Enter username here'
    onChangeText={text => setUsername(text)}
  />
  <Button
    mode='contained'
    onPress={() => 
      navigation.navigate('Game', {'username': username})
    }
  >
    Create
  </Button>
  ...

Game.js (relevant parts)

export default function Game({ navigation, route }) {
  return (
    <View>
      <Text>
        {route.params.username}
      </Text>
    </View>
  );
}

How do I pass in my username state variable using React hooks from my Main component to my Game component? Currently I'm getting the following error on the {route.params.username} line in Game.js:

Objects are not valid as a React child (found: object with keys {replace, push, pop, popToTop, goBack, navigate, reset, setParams, dispatch, isFocused, canGoBack, dangerouslyGetParent, dangerouslyGetState, addListener, removeListener, setOptions}).

I just want to pass in whatever the most recent string value of username is after the button in Main screen is clicked (which should navigate away to Game screen), but it seems like the passed in value is an object.


Solution

  • The code you posted in the question works fine, but I think I found the problem(s) from looking at your snack.

    The first thing is that your utils.createGame function expects one parameter username, but you pass navigation to it and username like this:

    onPress={() =>
      utils.createGame(navigation, username).then(payload => {
        navigation.navigate("Game", payload);
      })
    }
    

    So remove navigation as a parameter instead:

    onPress={() =>
      utils.createGame(username).then(payload => {
        navigation.navigate("Game", payload);
      })
    }
    

    The same thing goes for utils.addPlayer where the function takes two parameters, but you pass three.


    To give more context, but to also fix another problem, change how you define utils to this:

    const utils = {
      createGame: async function(username) {
        const gameId = cryptorandomstring({ length: 6, type: 'distinguishable' });
        const playerId = cryptorandomstring({ length: 10 });
    
        return { 'gameId': gameId, 'creatorId': playerId, 'username': username };
      },
    
      addPlayer: async function(username, gameId) {
        const playerId = cryptorandomstring({ length: 10 });
    
        return { 'gameId': gameId, 'playerId': playerId, 'username': username };
      }
    }
    

    Inside addPlayer you had a return statement like this:

    return { 'gameId': gameId, 'playerId': playerId, 'playerUserName': username };
    

    The problem with this as that inside Game.js you're destructuring username from the params and not playerUserName. So the destructured username variable won't hold the correct value.