From 5c884b325dcd0890792433e7dc6aaad7a161867a Mon Sep 17 00:00:00 2001 From: dsmith Date: Sat, 5 Apr 2025 17:00:39 -0400 Subject: [PATCH 1/7] Move the full code example into the How it will work section where it belongs --- versioned_docs/version-7.x/auth-flow.md | 75 ++++++++++++------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/versioned_docs/version-7.x/auth-flow.md b/versioned_docs/version-7.x/auth-flow.md index c127c2ca63..b5ea576ea2 100755 --- a/versioned_docs/version-7.x/auth-flow.md +++ b/versioned_docs/version-7.x/auth-flow.md @@ -35,39 +35,6 @@ We can configure different screens to be available based on some condition. For -To do this, we need a couple of things: - -1. Define two hooks: `useIsSignedIn` and `useIsSignedOut`, which return a boolean value indicating whether the user is signed in or not. -2. Use the `useIsSignedIn` and `useIsSignedOut` along with the [`if`](static-configuration.md#if) property to define the screens that are available based on the condition. - -This tells React Navigation to show specific screens based on the signed in status. When the signed in status changes, React Navigation will automatically show the appropriate screen. - -## Define the hooks - -To implement the `useIsSignedIn` and `useIsSignedOut` hooks, we can start by creating a context to store the authentication state. Let's call it `SignInContext`: - -```js -import * as React from 'react'; - -const SignInContext = React.createContext(); -``` - -Then we can implement the `useIsSignedIn` and `useIsSignedOut` hooks as follows: - -```js -function useIsSignedIn() { - const isSignedIn = React.useContext(SignInContext); - return isSignedIn; -} - -function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; -} -``` - -We'll discuss how to expose the context value later. - ```js name="Customizing tabs appearance" snack import * as React from 'react'; import { View } from 'react-native'; @@ -82,6 +49,7 @@ const useIsSignedOut = () => { return false; }; +// codeblock-focus-start const signedInStack = createNativeStackNavigator({ screens: { Home: HomeScreen, @@ -97,7 +65,6 @@ const signedOutStack = createNativeStackNavigator({ }, }); -// codeblock-focus-start const RootStack = createNativeStackNavigator({ screens: { LoggedIn: { @@ -146,9 +113,8 @@ function SignUpScreen() { ``` - -For example: + ```js name="Customizing tabs appearance" snack import * as React from 'react'; @@ -209,6 +175,9 @@ function SignUpScreen() { } ``` + + + When we define screens like this, when `isSignedIn` is `true`, React Navigation will only see the `Home`, `Profile` and `Settings` screens, and when it's `false`, React Navigation will see the `SignIn` and `SignUp` screens. This makes it impossible to navigate to the `Home`, `Profile` and `Settings` screens when the user is not signed in, and to `SignIn` and `SignUp` screens when the user is signed in. This pattern has been in use by other routing libraries such as React Router for a long time, and is commonly known as "Protected routes". Here, our screens which need the user to be signed in are "protected" and cannot be navigated to by other means if the user is not signed in. @@ -219,8 +188,38 @@ The example shows stack navigator, but you can use the same approach with any na By conditionally defining different screens based on a variable, we can implement auth flow in a simple way that doesn't require additional logic to make sure that the correct screen is shown. - - +To do this, we need a couple of things: + +1. Define two hooks: `useIsSignedIn` and `useIsSignedOut`, which return a boolean value indicating whether the user is signed in or not. +2. Use the `useIsSignedIn` and `useIsSignedOut` along with the [`if`](static-configuration.md#if) property to define the screens that are available based on the condition. + +This tells React Navigation to show specific screens based on the signed in status. When the signed in status changes, React Navigation will automatically show the appropriate screen. + +## Define the hooks + +To implement the `useIsSignedIn` and `useIsSignedOut` hooks, we can start by creating a context to store the authentication state. Let's call it `SignInContext`: + +```js +import * as React from 'react'; + +const SignInContext = React.createContext(); +``` + +Then we can implement the `useIsSignedIn` and `useIsSignedOut` hooks as follows: + +```js +function useIsSignedIn() { + const isSignedIn = React.useContext(SignInContext); + return isSignedIn; +} + +function useIsSignedOut() { + const isSignedIn = React.useContext(SignInContext); + return !isSignedIn; +} +``` + +We'll discuss how to expose the context value later. ## Define our screens From ee18a7166ba361beca3b15e30453c353e6640fe9 Mon Sep 17 00:00:00 2001 From: dsmith Date: Sat, 5 Apr 2025 17:09:01 -0400 Subject: [PATCH 2/7] Change Static and Dynamic tabs to better match each other --- versioned_docs/version-7.x/auth-flow.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/versioned_docs/version-7.x/auth-flow.md b/versioned_docs/version-7.x/auth-flow.md index b5ea576ea2..b3f8c0b014 100755 --- a/versioned_docs/version-7.x/auth-flow.md +++ b/versioned_docs/version-7.x/auth-flow.md @@ -83,13 +83,13 @@ const RootStack = createNativeStackNavigator({ }, }, }); -// codeblock-focus-end const Navigation = createStaticNavigation(RootStack); export default function App() { return ; } +// codeblock-focus-end function HomeScreen() { return ; @@ -124,35 +124,38 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); -const getIsSignedIn = () => { - // custom logic +const useIsSignedIn = () => { return true; }; +const useIsSignedOut = () => { + return false; +}; + +// codeblock-focus-start export default function App() { - const isSignedIn = getIsSignedIn(); + const isSignedIn = useIsSignedIn(); return ( - // codeblock-focus-start {isSignedIn ? ( - <> + - + ) : ( - <> + - + )} - // codeblock-focus-end ); } +// codeblock-focus-end function HomeScreen() { return ; From f7c01989782882616dbdda91c4cedc7c0d6543f9 Mon Sep 17 00:00:00 2001 From: dsmith Date: Sat, 5 Apr 2025 17:46:17 -0400 Subject: [PATCH 3/7] Make static and dynamic examples match better and change text to better reflect code --- versioned_docs/version-7.x/auth-flow.md | 26 ++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/versioned_docs/version-7.x/auth-flow.md b/versioned_docs/version-7.x/auth-flow.md index b3f8c0b014..8329d0c51f 100755 --- a/versioned_docs/version-7.x/auth-flow.md +++ b/versioned_docs/version-7.x/auth-flow.md @@ -46,7 +46,7 @@ const useIsSignedIn = () => { }; const useIsSignedOut = () => { - return false; + return !useIsSignedIn(); }; // codeblock-focus-start @@ -83,13 +83,13 @@ const RootStack = createNativeStackNavigator({ }, }, }); +// codeblock-focus-end const Navigation = createStaticNavigation(RootStack); export default function App() { return ; } -// codeblock-focus-end function HomeScreen() { return ; @@ -129,11 +129,11 @@ const useIsSignedIn = () => { }; const useIsSignedOut = () => { - return false; + return !useIsSignedIn(); }; -// codeblock-focus-start export default function App() { + // codeblock-focus-start const isSignedIn = useIsSignedIn(); return ( @@ -154,8 +154,8 @@ export default function App() { ); + // codeblock-focus-end } -// codeblock-focus-end function HomeScreen() { return ; @@ -181,11 +181,11 @@ function SignUpScreen() { -When we define screens like this, when `isSignedIn` is `true`, React Navigation will only see the `Home`, `Profile` and `Settings` screens, and when it's `false`, React Navigation will see the `SignIn` and `SignUp` screens. This makes it impossible to navigate to the `Home`, `Profile` and `Settings` screens when the user is not signed in, and to `SignIn` and `SignUp` screens when the user is signed in. +When `useIsSignedIn` returns `true`, React Navigation will only see the `Home`, `Profile` and `Settings` screens, and when it returns `false`, React Navigation will see the `SignIn` and `SignUp` screens. This makes it impossible to navigate to the `Home`, `Profile` and `Settings` screens when the user is not signed in, and to `SignIn` and `SignUp` screens when the user is signed in. This pattern has been in use by other routing libraries such as React Router for a long time, and is commonly known as "Protected routes". Here, our screens which need the user to be signed in are "protected" and cannot be navigated to by other means if the user is not signed in. -The magic happens when the value of the `isSignedIn` variable changes. Let's say, initially `isSignedIn` is `false`. This means, either `SignIn` or `SignUp` screens are shown. After the user signs in, the value of `isSignedIn` will change to `true`. React Navigation will see that the `SignIn` and `SignUp` screens are no longer defined and so it will remove them. Then it'll show the `Home` screen automatically because that's the first screen defined when `isSignedIn` is `true`. +The magic happens when the value returned by `useIsSignedin` changes. Let's say, initially `useIsSignedIn` returns `false`. This means, either `SignIn` or `SignUp` screens are shown. After the user signs in, the return value of `useIsSignedIn` will change to `true`. React Navigation will see that the `SignIn` and `SignUp` screens are no longer defined and so it will remove them. Then it'll show the `Home` screen automatically because that's the first screen defined when `useIsSignedIn` returns `true`. The example shows stack navigator, but you can use the same approach with any navigator. @@ -193,9 +193,21 @@ By conditionally defining different screens based on a variable, we can implemen To do this, we need a couple of things: + + + 1. Define two hooks: `useIsSignedIn` and `useIsSignedOut`, which return a boolean value indicating whether the user is signed in or not. 2. Use the `useIsSignedIn` and `useIsSignedOut` along with the [`if`](static-configuration.md#if) property to define the screens that are available based on the condition. + + + + +1. Define two hooks: `useIsSignedIn` and `useIsSignedOut`, which return a boolean value indicating whether the user is signed in or not. +2. Use the `useIsSignedIn` and `useIsSignedOut` along with conditional rendering to define the screens that are available based on the condition. + + + This tells React Navigation to show specific screens based on the signed in status. When the signed in status changes, React Navigation will automatically show the appropriate screen. ## Define the hooks From 5b36bbe9650fa6bace5c46f071f2ee8ce2fd8bea Mon Sep 17 00:00:00 2001 From: dsmith Date: Sat, 5 Apr 2025 17:59:56 -0400 Subject: [PATCH 4/7] Change Static and Dynamic tabs in Define our screens sections to make more sense --- versioned_docs/version-7.x/auth-flow.md | 57 +++---------------------- 1 file changed, 7 insertions(+), 50 deletions(-) diff --git a/versioned_docs/version-7.x/auth-flow.md b/versioned_docs/version-7.x/auth-flow.md index 8329d0c51f..bcf65c42a3 100755 --- a/versioned_docs/version-7.x/auth-flow.md +++ b/versioned_docs/version-7.x/auth-flow.md @@ -319,12 +319,6 @@ const RootStack = createNativeStackNavigator({ }); ``` -:::tip - -If you have both your login-related screens and rest of the screens in Stack navigators, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. - -::: - @@ -334,11 +328,12 @@ if (isLoading) { return ; } +const isSignedIn = userToken != null; + return ( - {userToken == null ? ( - // No token found, user isn't signed in + {isSignedIn ? ( ) : ( - // User is signed in )} @@ -356,9 +350,6 @@ return ( ); ``` - - - In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. After we get the token and if it's valid, we need to set the `userToken`. We also have another state called `isSignout` to have a different animation on sign out. The main thing to notice is that we're conditionally defining screens based on these state variables: @@ -368,40 +359,6 @@ The main thing to notice is that we're conditionally defining screens based on t Here, we're conditionally defining one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly, for the screens accessible after signing in, you probably have more than one screen. We can use `React.Fragment` to define multiple screens: - - - -```js -const SignInContext = React.createContext(); - -function useIsSignedIn() { - const isSignedIn = React.useContext(SignInContext); - return isSignedIn; -} - -function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; -} - -/* content */ - -export default function App() { - /* content */ - - const isSignedIn = userToken != null; - - return ( - - - - ); -} -``` - - - - ```js state.userToken == null ? ( <> @@ -417,15 +374,15 @@ state.userToken == null ? ( ); ``` + + + :::tip -If you have both your login-related screens and rest of the screens in two different Stack navigators, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. +If you have both your login-related screens and rest of the screens in Stack navigators, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. ::: - - - ## Implement the logic for restoring the token :::note From 5e6e25e90942048f52d46fe5ebeb93db18a32958 Mon Sep 17 00:00:00 2001 From: dsmith Date: Sat, 5 Apr 2025 18:03:32 -0400 Subject: [PATCH 5/7] Remove reference to isSignout state that doesn't show up anywhere --- versioned_docs/version-7.x/auth-flow.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/versioned_docs/version-7.x/auth-flow.md b/versioned_docs/version-7.x/auth-flow.md index bcf65c42a3..e5e1bd9487 100755 --- a/versioned_docs/version-7.x/auth-flow.md +++ b/versioned_docs/version-7.x/auth-flow.md @@ -288,7 +288,7 @@ return ( ); ``` -In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. After we get the token and if it's valid, we need to set the `userToken`. We also have another state called `isSignout` to have a different animation on sign out. +In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. Next, we're exposing the sign in status via the `SignInContext` so that it's available to the `useIsSignedIn` and `useIsSignedOut` hooks. @@ -350,7 +350,7 @@ return ( ); ``` -In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. After we get the token and if it's valid, we need to set the `userToken`. We also have another state called `isSignout` to have a different animation on sign out. +In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. The main thing to notice is that we're conditionally defining screens based on these state variables: @@ -360,7 +360,7 @@ The main thing to notice is that we're conditionally defining screens based on t Here, we're conditionally defining one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly, for the screens accessible after signing in, you probably have more than one screen. We can use `React.Fragment` to define multiple screens: ```js -state.userToken == null ? ( +isSignedIn ? ( <> From 7c12bbefe829e2bc8e97be139551c3cd3d44f728 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Thu, 17 Apr 2025 20:40:58 +0200 Subject: [PATCH 6/7] Update docs --- versioned_docs/version-7.x/auth-flow.md | 185 ++++++++---------------- 1 file changed, 57 insertions(+), 128 deletions(-) diff --git a/versioned_docs/version-7.x/auth-flow.md b/versioned_docs/version-7.x/auth-flow.md index e5e1bd9487..2d29c1efdc 100755 --- a/versioned_docs/version-7.x/auth-flow.md +++ b/versioned_docs/version-7.x/auth-flow.md @@ -30,12 +30,12 @@ We want the following behavior from our authentication flow: ## How it will work -We can configure different screens to be available based on some condition. For example, if the user is signed in, we can define `Home`, `Profile`, `Settings` etc. If the user is not signed in, we can define `SignIn` and `SignUp` screens. +We can configure different screens to be available based on some condition. For example, if the user is signed in, we want `Home` to be available. If the user is not signed in, we want `SignIn` to be available. -```js name="Customizing tabs appearance" snack +```js name="Authentication flow" snack import * as React from 'react'; import { View } from 'react-native'; import { createStaticNavigation } from '@react-navigation/native'; @@ -50,36 +50,15 @@ const useIsSignedOut = () => { }; // codeblock-focus-start -const signedInStack = createNativeStackNavigator({ - screens: { - Home: HomeScreen, - Profile: ProfileScreen, - Settings: SettingsScreen, - }, -}); - -const signedOutStack = createNativeStackNavigator({ - screens: { - SignIn: SignInScreen, - SignUp: SignUpScreen, - }, -}); - const RootStack = createNativeStackNavigator({ screens: { - LoggedIn: { + Home: { if: useIsSignedIn, - screen: signedInStack, - options: { - headerShown: false, - }, + screen: HomeScreen, }, - LoggedOut: { + SignIn: { if: useIsSignedOut, - screen: signedOutStack, - options: { - headerShown: false, - }, + screen: SignInScreen, }, }, }); @@ -95,28 +74,48 @@ function HomeScreen() { return ; } -function ProfileScreen() { +function SignInScreen() { return ; } +``` -function SettingsScreen() { - return ; -} +Here, for each screen, we have defined a condition using the `if` property which takes a hook. The hook returns a boolean value indicating whether the user is signed in or not. If the hook returns `true`, the screen will be available, otherwise it won't. -function SignInScreen() { - return ; +When `useIsSignedIn` returns `true`, React Navigation will only use the `Home` screen, and when it returns `false`, React Navigation will use the `SignIn` screen. This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. + +The magic happens when the value returned by `useIsSignedin` changes. Let's say, initially `useIsSignedIn` returns `false`. This means that `SignIn` screens is shown. After the user signs in, the return value of `useIsSignedIn` will change to `true`. React Navigation will see that the `SignIn` screen is no longer defined and so it will remove it. Then it'll show the `Home` screen automatically because that's the first screen defined when `useIsSignedIn` returns `true`. + +## Define the hooks + +To implement the `useIsSignedIn` and `useIsSignedOut` hooks, we can start by creating a context to store the authentication state. Let's call it `SignInContext`: + +```js +import * as React from 'react'; + +const SignInContext = React.createContext(); +``` + +Then we can implement the `useIsSignedIn` and `useIsSignedOut` hooks as follows: + +```js +function useIsSignedIn() { + const isSignedIn = React.useContext(SignInContext); + return isSignedIn; } -function SignUpScreen() { - return ; +function useIsSignedOut() { + const isSignedIn = React.useContext(SignInContext); + return !isSignedIn; } ``` +We'll discuss how to provide the context value later. + -```js name="Customizing tabs appearance" snack +```js name="Authentication flow" snack import * as React from 'react'; import { View } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; @@ -124,121 +123,47 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); -const useIsSignedIn = () => { - return true; -}; - -const useIsSignedOut = () => { - return !useIsSignedIn(); -}; - export default function App() { - // codeblock-focus-start - const isSignedIn = useIsSignedIn(); + const isSignedIn = true; return ( + // codeblock-focus-start {isSignedIn ? ( - - - - - + ) : ( - - - - + )} + // codeblock-focus-end ); - // codeblock-focus-end } function HomeScreen() { return ; } -function ProfileScreen() { - return ; -} - -function SettingsScreen() { - return ; -} - function SignInScreen() { return ; } - -function SignUpScreen() { - return ; -} ``` - - +Here, we have conditionally defined the screens based on the value of `isSignedIn`. -When `useIsSignedIn` returns `true`, React Navigation will only see the `Home`, `Profile` and `Settings` screens, and when it returns `false`, React Navigation will see the `SignIn` and `SignUp` screens. This makes it impossible to navigate to the `Home`, `Profile` and `Settings` screens when the user is not signed in, and to `SignIn` and `SignUp` screens when the user is signed in. +When `isSignedIn` is `true`, React Navigation will only see the `Home` screen, and when it returns `false`, React Navigation will see the `SignIn` screen. This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. This pattern has been in use by other routing libraries such as React Router for a long time, and is commonly known as "Protected routes". Here, our screens which need the user to be signed in are "protected" and cannot be navigated to by other means if the user is not signed in. -The magic happens when the value returned by `useIsSignedin` changes. Let's say, initially `useIsSignedIn` returns `false`. This means, either `SignIn` or `SignUp` screens are shown. After the user signs in, the return value of `useIsSignedIn` will change to `true`. React Navigation will see that the `SignIn` and `SignUp` screens are no longer defined and so it will remove them. Then it'll show the `Home` screen automatically because that's the first screen defined when `useIsSignedIn` returns `true`. - -The example shows stack navigator, but you can use the same approach with any navigator. - -By conditionally defining different screens based on a variable, we can implement auth flow in a simple way that doesn't require additional logic to make sure that the correct screen is shown. - -To do this, we need a couple of things: - - - - -1. Define two hooks: `useIsSignedIn` and `useIsSignedOut`, which return a boolean value indicating whether the user is signed in or not. -2. Use the `useIsSignedIn` and `useIsSignedOut` along with the [`if`](static-configuration.md#if) property to define the screens that are available based on the condition. - - - - - -1. Define two hooks: `useIsSignedIn` and `useIsSignedOut`, which return a boolean value indicating whether the user is signed in or not. -2. Use the `useIsSignedIn` and `useIsSignedOut` along with conditional rendering to define the screens that are available based on the condition. +The magic happens when the value of `isSignedin` changes. Let's say, initially `isSignedIn` returns `false`. This means that `SignIn` screens is shown. After the user signs in, the value of `isSignedIn` will change to `true`. React Navigation will see that the `SignIn` screen is no longer defined and so it will remove it. Then it'll show the `Home` screen automatically because that's the first screen defined when `isSignedIn` is `true`. -This tells React Navigation to show specific screens based on the signed in status. When the signed in status changes, React Navigation will automatically show the appropriate screen. -## Define the hooks +## Add more screens -To implement the `useIsSignedIn` and `useIsSignedOut` hooks, we can start by creating a context to store the authentication state. Let's call it `SignInContext`: - -```js -import * as React from 'react'; - -const SignInContext = React.createContext(); -``` - -Then we can implement the `useIsSignedIn` and `useIsSignedOut` hooks as follows: - -```js -function useIsSignedIn() { - const isSignedIn = React.useContext(SignInContext); - return isSignedIn; -} - -function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; -} -``` - -We'll discuss how to expose the context value later. - -## Define our screens - -In our navigator, we can conditionally define appropriate screens. For our case, let's say we have 3 screens: +For our case, let's say we have 3 screens: - `SplashScreen` - This will show a splash or loading screen when we're restoring the token. - `SignIn` - This is the screen we show if the user isn't signed in already (we couldn't find a token). @@ -357,7 +282,7 @@ The main thing to notice is that we're conditionally defining screens based on t - `SignIn` screen is only defined if `userToken` is `null` (user is not signed in) - `Home` screen is only defined if `userToken` is non-null (user is signed in) -Here, we're conditionally defining one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly, for the screens accessible after signing in, you probably have more than one screen. We can use `React.Fragment` to define multiple screens: +Here, we're conditionally defining one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly, for the screens accessible after signing in, you probably have more than one screen. We can use [`React.Fragment`](https://react.dev/reference/react/Fragment) or [`Group`](group.md) to define multiple screens: ```js isSignedIn ? ( @@ -374,15 +299,15 @@ isSignedIn ? ( ); ``` - - - :::tip -If you have both your login-related screens and rest of the screens in Stack navigators, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. +If you have both your login-related screens and rest of the screens in two different Stack navigators and render them conditionally, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. ::: + + + ## Implement the logic for restoring the token :::note @@ -393,8 +318,8 @@ The following is just an example of how you might implement the logic for authen From the previous snippet, we can see that we need 3 state variables: -- `isLoading` - We set this to `true` when we're trying to check if we already have a token saved in `SecureStore` -- `isSignout` - We set this to `true` when user is signing out, otherwise set it to `false` +- `isLoading` - We set this to `true` when we're trying to check if we already have a token saved in `SecureStore`. +- `isSignout` - We set this to `true` when user is signing out, otherwise set it to `false`. This can be used to customize the animation when signing out. - `userToken` - The token for the user. If it's non-null, we assume the user is logged in, otherwise not. So we need to: @@ -590,11 +515,11 @@ const RootStack = createNativeStackNavigator({ screen: HomeScreen, }, SignIn: { + if: useIsSignedOut, screen: SignInScreen, options: { title: 'Sign in', }, - if: useIsSignedOut, }, }, }); @@ -946,6 +871,10 @@ If you have a bunch of shared screens, you can also use [`navigationKey` with a +The examples above show stack navigator, but you can use the same approach with any navigator. + +By specifying a condition for our screens, we can implement auth flow in a simple way that doesn't require additional logic to make sure that the correct screen is shown. + ## Don't manually navigate when conditionally rendering screens It's important to note that when using such a setup, you **don't manually navigate** to the `Home` screen by calling `navigation.navigate('Home')` or any other method. **React Navigation will automatically navigate to the correct screen** when `isSignedIn` changes - `Home` screen when `isSignedIn` becomes `true`, and to `SignIn` screen when `isSignedIn` becomes `false`. You'll get an error if you attempt to navigate manually. From 93b8daaea027e3079721e940ccd8972fb0f655d0 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Thu, 24 Apr 2025 22:06:49 +0200 Subject: [PATCH 7/7] Improve auth flow docs --- versioned_docs/version-7.x/auth-flow.md | 150 +++++++++++++++++------- 1 file changed, 105 insertions(+), 45 deletions(-) diff --git a/versioned_docs/version-7.x/auth-flow.md b/versioned_docs/version-7.x/auth-flow.md index 2d29c1efdc..9f50596b3a 100755 --- a/versioned_docs/version-7.x/auth-flow.md +++ b/versioned_docs/version-7.x/auth-flow.md @@ -81,9 +81,21 @@ function SignInScreen() { Here, for each screen, we have defined a condition using the `if` property which takes a hook. The hook returns a boolean value indicating whether the user is signed in or not. If the hook returns `true`, the screen will be available, otherwise it won't. -When `useIsSignedIn` returns `true`, React Navigation will only use the `Home` screen, and when it returns `false`, React Navigation will use the `SignIn` screen. This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. +This means: -The magic happens when the value returned by `useIsSignedin` changes. Let's say, initially `useIsSignedIn` returns `false`. This means that `SignIn` screens is shown. After the user signs in, the return value of `useIsSignedIn` will change to `true`. React Navigation will see that the `SignIn` screen is no longer defined and so it will remove it. Then it'll show the `Home` screen automatically because that's the first screen defined when `useIsSignedIn` returns `true`. +- When `useIsSignedIn` returns `true`, React Navigation will only use the `Home` screen, since it's the only screen matching the condition. +- Similarly, when `useIsSignedOut` returns `true`, React Navigation will use the `SignIn` screen. + +This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. + +When the values returned by `useIsSignedin` and `useIsSignedOut` change, the screens matching the condition will change: + +- Let's say, initially `useIsSignedOut` returns `true`. This means that `SignIn` screens is shown. +- After the user signs in, the return value of `useIsSignedIn` will change to `true` and `useIsSignedOut` will change to `false`, which means: + - React Navigation will see that the `SignIn` screen is no longer matches the condition, so it will remove the screen. + - Then it'll show the `Home` screen automatically because that's the first screen available when `useIsSignedIn` returns `true`. + +The order of the screens matters when there are multiple screens matching the condition. For example, if there are two screens matching `useIsSignedIn`, the first screen will be shown when the condition is `true`. ## Define the hooks @@ -104,8 +116,7 @@ function useIsSignedIn() { } function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; + return !useIsSignedIn(); } ``` @@ -152,11 +163,21 @@ function SignInScreen() { Here, we have conditionally defined the screens based on the value of `isSignedIn`. -When `isSignedIn` is `true`, React Navigation will only see the `Home` screen, and when it returns `false`, React Navigation will see the `SignIn` screen. This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. +This means: + +- When `isSignedIn` is `true`, React Navigation will only see the `Home` screen, since it's the only screen defined based on the condition. +- Similarly, when `isSignedIn` is `false`, React Navigation will only see the `SignIn` screen. -This pattern has been in use by other routing libraries such as React Router for a long time, and is commonly known as "Protected routes". Here, our screens which need the user to be signed in are "protected" and cannot be navigated to by other means if the user is not signed in. +This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. -The magic happens when the value of `isSignedin` changes. Let's say, initially `isSignedIn` returns `false`. This means that `SignIn` screens is shown. After the user signs in, the value of `isSignedIn` will change to `true`. React Navigation will see that the `SignIn` screen is no longer defined and so it will remove it. Then it'll show the `Home` screen automatically because that's the first screen defined when `isSignedIn` is `true`. +When the value of `isSignedin` changes, the screens defined based on the condition will change: + +- Let's say, initially `isSignedin` is `false`. This means that `SignIn` screens is shown. +- After the user signs in, the value of `isSignedin` will change to `true`, which means: + - React Navigation will see that the `SignIn` screen is no longer defined, so it will remove the screen. + - Then it'll show the `Home` screen automatically because that's the first screen defined when `isSignedin` returns `true`. + +The order of the screens matters when there are multiple screens matching the condition. For example, if there are two screens defined based on `isSignedin`, the first screen will be shown when the condition is `true`. @@ -194,10 +215,46 @@ const RootStack = createNativeStackNavigator({ const Navigation = createStaticNavigation(RootStack); ``` + + + +```js +const Stack = createNativeStackNavigator(); + +export default function App() { + const isSignedIn = true; + + return ( + + + {isSignedIn ? ( + + ) : ( + + )} + + + ); +} +``` + + + + Notice how we have only defined the `Home` and `SignIn` screens here, and not the `SplashScreen`. The `SplashScreen` should be rendered before we render any navigators so that we don't render incorrect screens before we know whether the user is signed in or not. When we use this in our component, it'd look something like this: + + + ```js if (isLoading) { // We haven't finished checking for the token yet @@ -213,37 +270,6 @@ return ( ); ``` -In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. - -Next, we're exposing the sign in status via the `SignInContext` so that it's available to the `useIsSignedIn` and `useIsSignedOut` hooks. - -In the above example, we have one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly for the screens accessible after sign in, you probably have more than one screen. We can use [`groups`](static-configuration.md#groups) to define multiple screens: - -```js -const RootStack = createNativeStackNavigator({ - screens: { - // Common screens - }, - groups: { - SignedIn: { - if: useIsSignedIn, - screens: { - Home: HomeScreen, - Profile: ProfileScreen, - }, - }, - SignedOut: { - if: useIsSignedOut, - screens: { - SignIn: SignInScreen, - SignUp: SignUpScreen, - ResetPassword: ResetPasswordScreen, - }, - }, - }, -}); -``` - @@ -275,14 +301,49 @@ return ( ); ``` + + + In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. -The main thing to notice is that we're conditionally defining screens based on these state variables: +Next, we're exposing the sign in status via the `SignInContext` so that it's available to the `useIsSignedIn` and `useIsSignedOut` hooks. + +In the above example, we have one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly for the screens accessible after sign in, you probably have more than one screen. + + + + +We can use [`groups`](static-configuration.md#groups) to define multiple screens: + +```js +const RootStack = createNativeStackNavigator({ + screens: { + // Common screens + }, + groups: { + SignedIn: { + if: useIsSignedIn, + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, + }, + SignedOut: { + if: useIsSignedOut, + screens: { + SignIn: SignInScreen, + SignUp: SignUpScreen, + ResetPassword: ResetPasswordScreen, + }, + }, + }, +}); +``` -- `SignIn` screen is only defined if `userToken` is `null` (user is not signed in) -- `Home` screen is only defined if `userToken` is non-null (user is signed in) + + -Here, we're conditionally defining one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly, for the screens accessible after signing in, you probably have more than one screen. We can use [`React.Fragment`](https://react.dev/reference/react/Fragment) or [`Group`](group.md) to define multiple screens: +We can use [`React.Fragment`](https://react.dev/reference/react/Fragment) or [`Group`](group.md) to define multiple screens: ```js isSignedIn ? ( @@ -301,7 +362,7 @@ isSignedIn ? ( :::tip -If you have both your login-related screens and rest of the screens in two different Stack navigators and render them conditionally, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. +Instead of having your login-related screens and rest of the screens in two different Stack navigators and render them conditionally, we recommend to use a single Stack navigator and place the conditional inside. This makes it possible to have a proper transition animation during login/logout. ::: @@ -368,8 +429,7 @@ function useIsSignedIn() { } function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; + return !useIsSignedIn(); } function SplashScreen() {