Continuing from the last article, we are at a point where we want to build the components that a logged-in user can see. But we don’t want to start adding complex authentication handling to our application yet. This can be done by adding global state management system like Recoil to our application.
Introducing Recoil
Recoil is the state management library built by the Meta (Facebook) team. In the Card Games app, we’ll implement Recoil to manage the logged-in status of our users and some details about the games currently being played.
Considering it is still reasonably new (2020) to the public, you may prefer to use another tool for your applications.
Setting Up State With Recoil In Our App
To begin, we need to install Recoil:
npm install recoil
Once installed, we must wrap our application component with the Recoil component. This allows Recoil to be present on all child components. The Recoil wrapping component is called RecoilRoot.
Our App component will look something like this:
function App() {
return (
<RecoilRoot>
<ThemeProvider theme={theme}>
... app contents ...
</ThemeProvider>
</RecoilRoot>
)
}
The Logged In Status
Let’s start with a simple boolean value to denote whether a user is logged in. In Recoil, this will be done by creating an Atom. An Atom represents state in Recoil.
To create an Atom, you call the atom() function and pass a JSON value as the parameter. For example, our logged-in state can be represented as an atom like this:
import { atom } from 'recoil';
const loggedInState = atom({
key: 'loggedInState',
default: false,
});
Now that we have the loggedInState Atom ready to go, we need two more parts to make the Atom useful:
- a way to read the current value of loggedInState
- a way to update/set the value of loggedInState
Reading The Value Of A Recoil Atom
Use the hook useRecoilValue to retrieve an Atom’s value and subscribe your component to re-render if the value changes.
If we continue with our loggedInState Atom from above, you can get access to its value like this:
const loggedIn = useRecoilValue(loggedInState);
You can then use the variable loggedIn within your component to drive the UI.
Setting The Value Of A Recoil Atom
Setting the Atoms value is also done by using a hook. This hook is called useRecoilState. This hook will return the state value and the function to set the state value. Much like the useState hook does in React.
Let’s update our above example using the useRecoilState hook:
const [loggedIn, setLoggedIn] = useRecoilState(loggedInState);
With loggedIn and setLoggedIn, we determine whether someone is logged in and change their logged-in status.
Example Setting And Reading An Atom
Our NavBar currently has a button for logging in. Let’s update this to toggle whether a user is logged in.
We’ll start by using the useRecoilState hook with our loggedInState Atom.
From here, we’ll create a function that uses the setLoggedIn function to toggle the logged-in status.
Finally, we’ll add an onClick event to the login button to trigger the above function.
The logic below will change the button’s text from Login to Logout based on the users logged in status.
The final product looks like this:
export default function NavBar() {
const [loggedIn, setLoggedIn] = useRecoilState(loggedInState);
function toggleLogStatus() {
setLoggedIn((loggedIn) => !loggedIn);
}
return (
<Box sx={{ flexGrow: 1 }} >
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="menu"
sx={{ mr: 2 }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Card Games
</Typography>
<SignUpForm />
<Button onClick={toggleLogStatus} color="inherit">{loggedIn ? "Logout" : "Login"}</Button>
</Toolbar>
</AppBar>
</Box>
);
}
That is all that we need to do to save the logged-in status of our users. Let’s look at hiding the currently played games table for users that aren’t logged in.
Using Recoil Atoms In Other Components
We have a component:
<LiveGamesTable />
This component lists all the games that are currently happening. I will show this table when a user logs in.
Luckily, we now have an Atom that holds the logged-in status of users.
Let’s use this status to hide or show the table as appropriate.
Remembering the hooks mentioned above, our LiveGamesTable component must use the useRecoilValue hook to ascertain the users logged in status.
So, let’s start by importing what we need into the LiveGamesTable component:
import { useRecoilValue } from 'recoil';
import { loggedInState } from './App';
We now need to get the logged-in value from the loggedInState Atom. We can put this at the start of the LiveGamesTable function for use later:
function LiveGamesTable() {
const loggedInStatus = useRecoilValue(loggedInState);
And finally, we can do a boolean shortcut to only display the table to logged-in users:
{loggedInStatus &&
<Box...
And there you have it. We now have a scaffold for adding global state to our application. In the next article, I will add a more complex data structure (the details of the games currently being played) to the global state.
Thanks for reading! If you want to catch up with me directly, reach out on Twitter. And as always, you can find the source code on GitHub
P.S. You can see where this project started in my other article here