Reputation: 32127
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
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