Load the State from the Session
To make our login information persist we need to store and load it from the browser session. There are a few different ways we can do this, using Cookies or Local Storage. Thankfully the AWS Amplify does this for us automatically and we just need to read from it and load it into our application state.
Amplify gives us a way to get the current user session using the Auth.currentSession()
method. It returns a promise that resolves to the session object (if there is one).
Load User Session
Let’s load this when our app loads. To do this we are going to use another React hook, called useEffect. Since Auth.currentSession()
returns a promise, it means that we need to ensure that the rest of our app is only ready to go after this has been loaded.
To do this, let’s add another state variable to our src/App.tsx
state called isAuthenticating
. Add it to the top of our App
function.
const [isAuthenticating, setIsAuthenticating] = useState(true);
We start with the value set to true
because as we first load our app, it’ll start by checking the current authentication state.
To load the user session we’ll add the following to our src/App.tsx
right below our variable declarations.
useEffect(() => {
onLoad();
}, []);
async function onLoad() {
try {
await Auth.currentSession();
userHasAuthenticated(true);
} catch (e) {
if (e !== "No current user") {
alert(e);
}
}
setIsAuthenticating(false);
}
Then include the Auth
module by adding the following to the header of src/App.tsx
.
import { Auth } from "aws-amplify";
Let’s make sure to include the useEffect
hook by replacing the React import in the header of src/App.tsx
with:
import { useState, useEffect } from "react";
Let’s understand how this and the useEffect
hook works.
The useEffect
hook takes a function and an array of variables. The function will be called every time the component is rendered. And the array of variables tell React to only re-run our function if the passed in array of variables have changed. This allows us to control when our function gets run. This has some neat consequences:
- If we don’t pass in an array of variables, our hook gets executed every time our component is rendered.
- If we pass in some variables, on every render React will first check if those variables have changed, before running our function.
- If we pass in an empty list of variables, then it’ll only run our function on the FIRST render.
In our case, we only want to check the user’s authentication state when our app first loads. So we’ll use the third option; just pass in an empty list of variables — []
.
When our app first loads, it’ll run the onLoad
function. All this does is load the current session. If it loads, then it updates the isAuthenticating
state variable once the process is complete. It does so by calling setIsAuthenticating(false)
. The Auth.currentSession()
method throws an error No current user
if nobody is currently logged in. We don’t want to show this error to users when they load up our app and are not signed in. Once Auth.currentSession()
runs successfully, we call userHasAuthenticated(true)
to set that the user is logged in.
So the top of our App
function should now look like this:
function App() {
const [isAuthenticating, setIsAuthenticating] = useState(true);
const [isAuthenticated, userHasAuthenticated] = useState(false);
useEffect(() => {
onLoad();
}, []);
...
Render When the State Is Ready
Since loading the user session is an asynchronous process, we want to ensure that our app does not change states when it first loads. To do this we’ll hold off rendering our app till isAuthenticating
is false
.
We’ll conditionally render our app based on the isAuthenticating
flag.
Replace the return
statement in src/App.js
with the following.
return (
!isAuthenticating && (
<div className="App container py-3">
<Navbar collapseOnSelect bg="light" expand="md" className="mb-3 px-3">
<LinkContainer to="/">
<Navbar.Brand className="fw-bold text-muted">Scratch</Navbar.Brand>
</LinkContainer>
<Navbar.Toggle />
<Navbar.Collapse className="justify-content-end">
<Nav activeKey={window.location.pathname}>
{isAuthenticated ? (
<Nav.Link onClick={handleLogout}>Logout</Nav.Link>
) : (
<>
<LinkContainer to="/signup">
<Nav.Link>Signup</Nav.Link>
</LinkContainer>
<LinkContainer to="/login">
<Nav.Link>Login</Nav.Link>
</LinkContainer>
</>
)}
</Nav>
</Navbar.Collapse>
</Navbar>
<AppContext.Provider
value={{ isAuthenticated, userHasAuthenticated } as AppContextType}
>
<Routes />
</AppContext.Provider>
</div>
)
);
Now if you head over to your browser and refresh the page, you should see that a user is logged in.
Unfortunately, when we hit Logout and refresh the page; we are still logged in. To fix this we are going to clear the session on logout next.
For help and discussion
Comments on this chapter