So, I've just upgraded an ignite-bowser project to their 5.0 template, which includes React Navigation 5, which requires a change from the legacy recommended method of using a SwitchNavigator to swap between 'Auth' and 'App' StackNavigators, to a new declarative auth flow approach that makes the SwitchNavigator redundant.
FYI, Ignite Bowser projects are essentially React Native template apps backed by
React Native
React Navigation
MobX State Tree
TypeScript
Reactotron
And more!
So this all seems simple enough, but I have not been able to get the actual navigators to switch when using a boolean stored in one of the App stores, and set to true
in an action called within the authentication method.
According to server logs, and Reactotron feed, authentication works fine. Reloading the app afterward does render the App navigator, but the session isn't actually valid since memory was cleared. All of the subsequent requests fail, but the application doesn't switch to the Auth navigator.
Here are the relevant code snippets:
root-navigator.tsx
const RootStack = () => {
const { pbidStore } = useStores()
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
gestureEnabled: true,
stackPresentation: "modal",
}}
>
{pbidStore.isAuthenticated ? (
<Stack.Screen
name="pbidStack"
component={PbidNavigator}
options={{
headerShown: false,
}}
/>
) : (
<Stack.Screen
name="authStack"
component={AuthNavigator}
options={{
headerShown: false,
}}
/>
)}
</Stack.Navigator>
/**
* PbidStore Model
*/
export const PbidStoreModel = types.model("PbidStore").props({
....
isAuthenticated: types.optional(types.boolean, false),
})
.actions(self => ({
setStatus(value?: "idle" | "pending" | "done" | "error") {
self.status = value
},
setAuthToken(token: string) {
self.environment.pbidApi.setAuthToken(token)
},
setAuthenticated(value: boolean) {
self.isAuthenticated = value
},
...
}))
.actions(self => ({
authenticate: flow(function*(email: string, password: string, remember: boolean) {
self.setStatus("pending")
try {
const result = yield self.environment.pbidApi.authenticate(email, password)
if (result.kind === "ok") {
self.setAuthToken(result.token)
self.setStatus("done")
self.setAuthenticated(true)
self.loadUser()
if(remember)
yield self.storeCredentials(email, password)
} else {
self.setStatus("error")
self.setAuthenticated(false)
}
} catch {
self.setStatus("error")
self.setAuthenticated(false)
}
}),
...
After I had written out this question, and started picking SO tags, and had to decide upon using mobx-react
vs mobx-react-lite
or both, I remembered the issues I ran into during the last upgrade I went through, which switched between those two, and the use of inject
and observer
.
So I realized that maybe my navigator needed to be observable...
Importing mobx-react-lite
and wrapping RootStack in the following fixed everything for me.
const RootStack = observer(() => {
Hope this helps save someone else the headache.
All in all, I'm pleased with what all these recent changes to react-native
with the Hooks and FunctionalComponents
, and what its done to associated libraries and eventually my code base, but wooof is it tiring having to constantly learn new API's and re-base my projects before they're even complete!