The useContext Hook: A Useful Way to Manage State
Tags: react, javascript, webdev
Published: 2024-05-15 02:24:25

If you’re familiar with React, then you know data only flows one way from parent components to child components. If you want to make a state available to child Components then you have to continuously pass it down as props to the child components and even grandchild Components. You can imagine that this process would become tedious if your component tree has many, many children.

You then risk your code becoming illegible or you might risk your code breaking in some way if you happen to make a typo or rename your props in some way when passing it down. Then this would now necessitate very, very thorough backtracking and waste valuable time that you could spend adding new features or refining your app, and nobody wants to waste time like that.

This is where useContext comes in!

In essence, the useContext hook makes a state globally available. You can inherit that state within any component. No longer do you have to occupy yourself with the task of passing down a state continuously. Now you can initialize a state or set a state within any component and have that state readily available in any component you choose.

So how do we do that?

Let’s say we want to initialize a simple state like isAuth with its setter setAuth and make that available in any component we choose. Then, to start, we need to make a .jsx file and import these two things:

`import { createContext, useState } from "react";`
We need createContext to, well, create a Context and deem something as a Context and the naming convention for this is camel case, but specifically a type of camel case where the first word is also capitalized, so ours specifically would be called something like AuthContext. Since we want to export a boolean value and a state setter, which is a function, we want to put the “default values” into createContext. Note that these “default values” will never really be used and instead just serve as a pseudo-placeholder to define the data type of what’s being exported.

So since we’re exporting a variable and function, we want to make sure that these two things are not defined, or are empty in the initial createContext function:

`export const AuthContext = createContext({
       isAuth:null,
       setAuth:()=>{}
});`
As you can see, isAuth is set to null and setAuth is set to an empty function, however, we have defined our exports as isAuth and setAuth. We will define what these two are in our Provider which is the part of useContext that allows us to pass down this state to child components. I will elaborate on this below, but the Provider exists so that we can wrap the components that we want to make this context available in, and we usually warp the <App> component with the Provider.

The naming convention is also the same type of camel case as the context. Inside the Provider, we then need to actually initialize the state and state setter with useState that we want to export, keeping the same names as we defined in createContext.

Then we create an Object called value that contains both the initialized state and state setter which the actual thing that we will export via the Provider. We then, in the return statement for the Provider, pass down the value Object as a prop. We also, in the parameters of the Provider, destructure a children prop so that we can wrap it with the Provider in the return statement to signify that anything wrapped by the Provider will inherit the state and state setter (or any other function) that we are exporting.

See the below for the implementation of our example:

`export const AuthProvider = ({children})=>{
   const [isAuth, setAuth] = useState(false);
   const value = {isAuth, setAuth};
return(
     <AuthContext.Provider value={value}>{children}     
                                        </AuthContext.Provider>
     );
};`
As you can see, in the return statement, we must export the Provider with the following name: <ContextName>.Provider. In our example this would be “AuthContext.Provider”.

In order to make this state available in every component, we must go to our index.js and wrap our <App /> Component with the Provider, like so:

`const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
   <AuthProvider>
      <App />
   </AuthProvider>
);`
This basically represents that we want to make the AuthContext available in the App component, which contains every component of our project, via our Provider, AuthProvider. Now you can access the state and state setter within any component!

To access it within our child components, we must finally use the useContext hook and also import Context, AuthContext, inside it.

Let’s say we have a child component that we want to import AuthContext into. To do so, we must need these two import statements in our child component:

`import { AuthContext } from "./components/context/auth.context";
import { useContext } from "react";`
We import useContext to actually access the contents of AuthContext.

Now, in order to access the specific contents of our AuthContext, we must use useContext to destructure its contents like so:

`const { isAuth, setAuth } = useContext(AuthContext);`
Remember that since our export is the value Object that we can destructure it like how we did above.

Now we can go nuts inside our child Component and use the state or set the state however we want!