One huge pain point in React (and NextJS) is having to pass props through multi-level components. The bigger the application or project gets, the more painful this becomes. This is because of how React is structured, in a tree like fashion with data only flowing in one direction.
There are multiple ways to solve this with things like
- React Redux
- MobX
- ...and more
One of the newer ways, especially introduced with React Hooks, is the React Context API.
What is the Context API?
It is a way to enable multiple components (or pages in NextJS) to share global variables and data without having to "pass" these as props. Think of it like a global "object" which stores data.
Although the Context API can be used for most cases, it shouldn't be used for everything. A close observation on component re-renders should be taken when using Context API to prevent any compromising leaks or inefficiencies.
That being said, we can easily implement Context API using React Hooks into NextJS.
Let's step through the creation of a simple custom Auth to allow our NextJS app to see the logged in status of a user, and the users details throughout every component and page.
1. Creating a Provider component
This component will provide our defined data to the rest of our app.
import { useState, createContext } from "react";
export const AuthContext = createContext();
const AuthProvider = ({ children }) => {
const [ loggedIn, setLoggedIn ] = useState(false);
const [ userDetails, setUserDetails ] = useState([]);
const login = value => {
setLoggedIn(true);
setUserDetails({
name: "Alan",
notifications: 3
});
}
const logout = value => {
setLoggedIn(false);
setUserDetails([]);
}
const contextValue = {
status: {
loggedIn,
login,
logout
},
user: {
userDetails,
setUserDetails
}
};
return (
<AuthContext.Provider value={ contextValue }>
{ children }
</AuthContext.Provider>
);
};
export default AuthProvider;
- The
createContext()
method, allows us to create a context object to hold some global data. - I've set up some state variables using the useState hook, including a variable for logged in, and a variable for the user details.
- Custom login/logout functions created to set the logged in status and also the user details (for this example, when logging in, we just mimic it with hard-coded values).
- I've set up a
contextValue
object which we can use to structure our global data nicely, for example, for theloggedIn
variable and functions, I've called it status, which means, further down the React Tree of components we will be able to access the loggedIn state variable likeAuthContext.status.loggedIn
which we will see shortly. - A Provider component will always return the children, in our case, the children will be the component or page, wrapped with our
AuthContext.Provider
.
We can save this Provider in a providers
folder or utils
or anything you like.
2. Wrap our app root component with our Context
In traditional React, we would wrap our <App />
with the Context, however, in NextJS, this doesn't exist. Instead we can provide a custom App component by creating a file in the root folder called _app.js
, it should already be present in most cases.
Let's first import our AuthProvider
component:
import AuthProvider from "../utils/AuthProvider";
Then wrap our <Component .... />
with this Provider. At this point then the Component will be passed as a child to our Context API Provider, which we saw above with the return of children.
return (
<AuthProvider>
<Component { ...pageProps } />
</AuthProvider>
);
3. Import useContext and our custom AuthContext into index.js
In order to use the values from our Provider, we need to utilise the useContext
hook from React, as well as importing our AuthContext
we created earlier:
import { useContext } from "react";
import { AuthContext } from "../utils/AuthProvider";
Inside our Home (or any other) component/page we can easily grab all of our Context global variables using the above hook, and destruct our custom, nicely formatted values like this:
const { status, user } = useContext(AuthContext);
At this point, we now have access, globally to the variables we set up earlier, e.g. { status.loggedIn }
will return the loggedIn value held in that state variable.
4. Setting up a simple login / logout UI
Using our global Context API variables/functions and data, we can now manipulate our UI based on this, let's set up a very basic, mimicked example of this:
Example of our return in index.js
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>
<h4>
Status:
<span style={{ color: status.loggedIn ? "green" : "red" }}>
{ status.loggedIn ? "Logged in" : "Not Logged In" }
</span>
</h4>
{ status.loggedIn && <button onClick={ status.logout }>Logout</button> }
{ !status.loggedIn && <button onClick={ status.login }>Login</button> }
{ status.loggedIn &&
<h3>Hi { user.userDetails.name }</h3>
}
</main>
Logged out state:
Logged in state:
Keep in mind, our Context API global "object" or data is accessible by every page/component. So in this case, if we were to navigate to /account
for example, the state would be retained and accessible in the same, global way.
This was a very very basic example of how to set up a Context API provider, and inject it into our NextJS app, then use the accessible functions and data based on the state, I hope this helps anyone, if it did, leave a little reaction on this post, greatly appreciated!
I've also recently started a YouTube channel. I'd love to see you over there and I welcome new subscribers. I'm posting coding tutorials, mostly on JavaScript, React and NextJS! ๐๐๐๐
Alan Montgomery - Coding Tutorials
Thanks for reading! Leave a comment below if you liked this!