4

In React Navigation 6, my research shows that to navigate without a prop I should make a reference and use createNavigationContainerRef. I'm able to pass down the screen name to my dispatch but for some reason when I evaluate the condition with isReady I'm always told it isn't. The code:

App.js:

import React from 'react'
import { NavigationContainer } from '@react-navigation/native'
import 'react-native-gesture-handler'

// Provider
import { Provider as AuthProvider } from './src/context/AuthContext'

// Navigation
import { navigationRef } from './src/navigation/NavRef'

// Screens
import ResolveAuthScreen from './src/screens/ResolveAuthScreen'

const App = () => {
  return (
    <AuthProvider>
      <NavigationContainer ref={navigationRef}>
        <ResolveAuthScreen />
      </NavigationContainer>
    </AuthProvider>
  )
}

export default App

ResolveAuthScreen.js:

import React, { useEffect, useContext } from 'react'

// Context
import { Context as AuthContext } from '../context/AuthContext'

const ResolveAuthScreen = () => {
  const { tryLocalSignIn } = useContext(AuthContext)

  useEffect(() => {
    tryLocalSignIn()
  }, [])

  return null
}
export default ResolveAuthScreen

AuthContext.js (stripped down):

import AsyncStorage from '@react-native-async-storage/async-storage'

// Context
import createContext from './createContext'

// Nav
import * as NavRef from '../navigation/NavRef'

const authReducer = (state, action) => {
  switch (action.type) {
    case 'signin':
      return { errorMessage: '', token: action.payload }
    case 'clear_error':
      return { ...state, errorMessage: '' }
    default:
      return state
  }
}

const tryLocalSignIn = dispatch => async () => {
  const token = await AsyncStorage.getItem('token')
  console.log({ token }) // renders token
  if (token) {
    dispatch({ type: 'signin', payload: token })
    NavRef.navigate('TrackListScreen')
  } else {
    NavRef.navigate('SignUp')
  }
}

export const { Provider, Context } = createContext(
  authReducer,
  { tryLocalSignIn },
  { token: null, errorMessage: '' },
)

NavRef.js:

import { createNavigationContainerRef } from '@react-navigation/native'

export const navigationRef = createNavigationContainerRef()

export function navigate(name, params) {
  console.log({ name, params })
  if (navigationRef.isReady()) {
    console.log('ready')
    console.log({ name, params })
    navigationRef.navigate('TrackDetailScreen', { name, params })
  } else {
    console.log('not ready')
  }
}

When I log the token from dispatch I get back the token. When I log the screen I get back TrackListScreen from navigate but whenever it's fired it always returns the console log of not ready.

Docs:

"dependencies": {
    "@react-native-async-storage/async-storage": "~1.15.0",
    "@react-navigation/bottom-tabs": "^6.0.9",
    "@react-navigation/native": "^6.0.6",
    "@react-navigation/native-stack": "^6.2.5",
    "axios": "^0.24.0",
    "expo": "~43.0.0",
    "expo-status-bar": "~1.1.0",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-native": "0.64.2",
    "react-native-elements": "^3.4.2",
    "react-native-gesture-handler": "~1.10.2",
    "react-native-reanimated": "~2.2.0",
    "react-native-safe-area-context": "3.3.2",
    "react-native-screens": "~3.8.0",
    "react-native-web": "0.17.1"
  },

Why is my navigate not working after my dispatch or why does the isReady false?

DᴀʀᴛʜVᴀᴅᴇʀ
  • 7,681
  • 17
  • 73
  • 127

1 Answers1

2

I'm having the same issue. When trying to access the exported navigationRef.isReady() from a redux-saga file, it always returns false. I'm not sure this is a safe approach, nor have I properly tested this, but the following workaround seems to work for me:

App.js

import {setNavigationRef, navigationIsReady} from './NavigationService';

const navigationRef = useNavigationContainerRef();

return (
      <NavigationContainer 
          ref={navigationRef}
          onReady={() => {
              setNavigationRef(navigationRef);
          }}> 
          ...
      </NavigationContainer>
);

NavigationService.js

export let navigationRefCopy = undefined;

export function setNavigationRef(navigationRef) {
    navigationRefCopy = navigationRef;
}

export function navigationIsReady() {
    return navigationRefCopy?.isReady(); // returns true when called in a redux saga file.
}