Ben Fortune
Ben Fortune

Reputation: 32127

Navigating to other screens not working with programmatic routes

I have a Routes component that returns Stack.Screen routes based on user state, or a loading screen depending on a loading state coming from a context.

For some reason when calling navigation.nagivate to change screens, it just redirects back to whatever the initialRouteName was.

I suspect it's because the loader isn't returning any routes and the navigation.navigate call is lost somewhere, but I'm unsure.

MVCE https://codesandbox.io/s/morning-glitter-b8w5j?file=/src/App.js

Routes.js

function Routes() {
  const { loading } = useContext(AppContext);
  const { user } = useContext(AuthContext);

  if (loading) {
    return <Text>Loading</Text>;
  }

  return (
    <NavigationContainer>
      {user ? (
        <Stack.Navigator initialRouteName="Home">
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Order" component={OrderScreen} />
        </Stack.Navigator>
      ) : (
        <Stack.Navigator initialRouteName="Login">
          <Stack.Screen name="Login" component={LoginScreen} />
        </Stack.Navigator>
      )}
    </NavigationContainer>
  );
}

OrderScreen.js (The route in question that won't work)

function OrderScreen({ order_id }) {
  const { order, getOrder } = useContext(OrderContext);
  useEffect(() => {
    getOrder(order_id);
  });
  return (
    <View>
      <Text>Order</Text>
      <Text>{order}</Text>
    </View>
  );
}

OrderProvider.js (where getOrder is called from)

const OrderContext = createContext({});
function OrderProvider({ children }) {
  const [order, setOrder] = useState(null);
  const { setLoading } = useContext(AppContext);
  return (
    <OrderContext.Provider
      value={{
        order,
        setOrder,
        getOrder: (orderId) => {
          setLoading(true);
          setTimeout(() => {
            setOrder(orderId);
            setLoading(false);
          }, 1000);
        }
      }}
    >
      {children}
    </OrderContext.Provider>
  );
}

Upvotes: 0

Views: 43

Answers (1)

Guruparan Giritharan
Guruparan Giritharan

Reputation: 16334

The problem is not with navigation is with the 'loading' state that you have in the context.

So when you open the Order screen you set the loading to true in the AppContext which causes the NavigationContainer in routes to re render which resets the navigation state.

const OrderContext = createContext({});
function OrderProvider({ children }) {
  const [order, setOrder] = useState(null);
  const [orderLoading, setOrderLoading] = useState(false);
  const { setLoading } = useContext(AppContext);
  return (
    <OrderContext.Provider
      value={{
        order,
        setOrder,
        orderLoading,
        getOrder: (orderId) => {
          setOrderLoading(true);
          setTimeout(() => {
            setOrder(orderId);
            setOrderLoading(false);
          }, 1000);
        }
      }}
    >
      {children}
    </OrderContext.Provider>
  );
}

You will have to have a loading in the order context which will solve this issue. And re rendering the whole navigation container is not a good idea.

The snack has a working example which has a loading only for the order screen

https://snack.expo.io/@guruparan/9be728

Upvotes: 1

Related Questions