DEV Community

loading...
Cover image for useContext for better state management!

useContext for better state management!

Yogini Bende
Front End Developer| Javascript Enthusiast | ReactJS | ReactNative | TailwindCSS | CSS | Exploring AWS | Learning Node! | Follow on Twitter for daily updates
・5 min read

Hi folks,

Managing data in an app is little tricky when many components are sharing and updating it. useState, useReducer, useMemo etc. are some state management hooks in React, which are very efficient and have their own ways to work in different scenarios. Though all these hooks are effective, there are still some situations where managing state becomes difficult.

If you already know why we need context hook, you can directly jump to its implementation here

Consider an app which works on user’s data. On loading, the app fetches this data from a backend and stores it in an app component. Later, this data is shared between many other children components. If child components are just using that data, then it’s fine. But, problems will arise, if one of them will update the data.

As we know, the app component fetches the data, we need to use prop drilling to share it with all the children. In this case, we create a prop userData and pass it to all the children of this app component, making it look something like this -

Alt Text

This approach works when the children are just consuming the data and not updating it. But if you see in the diagram, the children four is performing an update operation on our user data. After this update, the new version of data should be made available to all the other components.

If you notice, this transaction of data becomes fairly difficult when the app is complex and there are multiple states to handle across multiple components.

These are the scenarios where state management libraries like Redux are introduced in the app. But with React context in hand, we can do the state management efficiently and natively.

P.S Redux is a very good and very vast state management system. It is the best choice for complex applications. But if the app has only a few shared states, I prefer working with context over Redux.

What is Context?

React context is nothing but a global state to the app. It is a way to make a particular data available to all the components no matter how they are nested. Context helps you broadcast the data and changes happening to that data, to all the components. That’s why it is a very useful state management hook, when it comes to use cases like we discussed above.

You can read more on React context in the official documentation of react

How to use it?

Now that we understand the what and why behind a context. Let’s understand how we can use it. To create a context in any React app, you need to follow 4 simple steps -
1- Create a context
2- Create a provider
3- Add provider to the app
4- UseContext

These terms can become super confusing in the start. The best way to understand context is, consider it as a simple state, a state which we create using useState. The only thing context will do is to share this state and its changes throughout the app.

Hence, when we say, we are creating a context, we are creating a state! When we say we are creating a provider, as its name says, we are creating a wrapper component to provide that state to all the components. It is that simple!

Now, let's dive into code and create a context! In the below code, we will be covering step 1 and 2.

// UserDetailsProvider.js

import { createContext, useState } from 'react';

//create a context, with createContext api
export const userDetailsContext = createContext();

const UserDetailsProvider = (props) => {
        // this state will be shared with all components 
    const [userDetails, setUserDetails] = useState();

    return (
                // this is the provider providing state
        <userDetailsContext.Provider value={[userDetails, setUserDetails]}>
            {props.children}
        </userDetailsContext.Provider>
    );
};

export default UserDetailsProvider;
Enter fullscreen mode Exit fullscreen mode

In the code above, we have used createContext api to create our userDetailsContext. Now, the context got created, so we will need to create a provider.

In the function UserDetailsProvider, we created a provider for userDetailsContext. <contextname.Provider> is a common syntax for creating it. Please note a value prop here. The value prop will be used always to pass the shared state down. In this case, we are passing both state and setState functions down. This is because, even though any component updates the state, the global state can get updated which will be available for all the components.

Now that our context and provider are created. Let’s add the provider to the app. This is the most important step, as it will make the provider available to all components. Hence, let’s wrap our app component inside this provider. Our app component will look something like this -

//App Component

import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { RouteWithSubRoutes } from './utils/shared';
import UserDetailsProvider from './context/UserDetailsProvider';
import routes from './Routes';

function App() {
    return (
        <BrowserRouter>
            <Switch>
                // As login do not require the userDetails state, keeping it outside.
                <Route path='/' component={Login} exact />
                // All other routes are inside provider
                <UserDetailsProvider>
                    {routes.map((route) => (
                        <RouteWithSubRoutes key={route.key} {...route} />
                    ))}
                </UserDetailsProvider>
            </Switch>
        </BrowserRouter>
    );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

In this code, the data will not be fetched by the app component. Note, here we are adding only those components inside UserDetailsProvider which actually needs this state.

So here we come to the last part, using this context in any component. You must have guessed, this step needs the hook useContext as we will be using a context here! (No claps on guessing 😛)

This is done in the same way as we declare a state using useState. Something like this -

// Profile.js

import { useEffect, useState, useContext } from 'react';
import { getUser } from '../../utils/api';
import { userDetailsContext } from '../../context/UserDetailsProvider';

const Profile = ({ email }) => {

  // This is how we useContext!! Similar to useState
    const [userDetails, setUserDetails] = useContext(userDetailsContext);
    const [loading, setLoading] = useState(false);

    const handleGetUser = async () => {
        try {
            setLoading(true);
            let response = await getUser(email);
            setUserDetails(response.data);
        } catch (error) {
            console.log(error);
            // TODO: better error handling
        }
        setLoading(false);
    };

    useEffect(() => {
        if (!userDetails) {
            handleGetUser();
        }
    }, []);

    return <div className='bg-gray-gray1 h-full'>// do something</div>;
};

export default Profile;
Enter fullscreen mode Exit fullscreen mode

If you have noticed, the useContext looks similar to useState. And later we will be using it same as useState!! Hence, whenever the setUserDetails function is called, the state change will be effective throughout the app, saving too much prop drilling.

So, that’s all about useContext hook. I have also seen many examples of context api being used in toggling and setting themes for an app. Do share your use-cases for using this context api.

Thank you so much for reading this article and please let me know your comments/feedback/suggestions.

Keep learning 🙌

Discussion (23)

Collapse
luubinhan profile image
Lưu Bình An

You get into the issue with re-render soon, believed me I try to useContext as you mention, then the nightmere visit me when I do think complex

  • it is not designed for frequent updates
  • async operations are tricky
  • it has performance limitations
  • does not support selectors
  • you cannot prevent re-renders
  • correctly handling provider-less context is hard
Collapse
ratneshmurugesan profile image
Ratnesh Murugesan • Edited

Which alternate concept are you preferring / using nowadays?

Collapse
luubinhan profile image
Lưu Bình An
Collapse
vaibhavkhulbe profile image
Vaibhav Khulbe

React Hooks like useContext are definitely preferred over a somewhat complex Redux for state management.

I also liked this:

the useContext looks similar to useState. And later we will be using it same as useState!! Hence, whenever the setUserDetails function is called, the state change will be effective throughout the app, saving too much prop drilling.

Collapse
ms_yogii profile image
Yogini Bende Author

Thank you 🙌

Collapse
endrureza profile image
Endru Reza

Somehow, i just think that useContext might be a good idea when you have small or medium apps. But then, the code grows larger and hard to maintain i use redux for that.

Collapse
ms_yogii profile image
Yogini Bende Author

That is absolutely true and I have mentioned that in the article as well. Context can never be an alternative to any state management library like Redux, but it is good option when the app is fairly simple.

Collapse
ngprnk profile image
Nischal Gautam

Why not use Zustand it has a similar api to useContext ? useContext is not meant for state management since it can cause unnecessary re-renders?
I recommend to check this out. Zustand can be used like redux but it has much more simple API, which can highly reduce development time and improve dev productivity.
github.com/pmndrs/zustand

Collapse
burhanuday profile image
Burhanuddin Udaipurwala

yup. this is the correct answer. context is meant to be a dependency injection solution and not for state management.

Collapse
manoharreddyporeddy profile image
Manohar Reddy Poreddy

1

Article has too many ! and !!
Ideally in such cases, you user should also feel same emotion :) but that's not true for this article.
Example for articles see javascript.info/object here the ! is used to as a surprise, etc.

2

Article can be take simpler examples.
Leaving all the surrounding code out, explaining only the core concept, It won't be more than 20 lines. Once that is done, you can do more lines of explanation of real world example.

Hope the feedback helps! Otherwise please ignore!! :D

+1 for writing article though :)

Collapse
ianwijma profile image
Ian Wijma

I love using context. It's so much easier than any state management lib I've used.

Collapse
aslemammad profile image
M. Bagher Abiat

It was a great article, thanks for writing.

I'm a big fan of React Context, but it's not a state management solution, it's made for sharing data between children and dependency injection, and not state management. I suggest using jotai or zustand for state management instead.

Collapse
afozbek profile image
Abdullah Furkan Özbek

Yes, as a team we also choose to use useContext for state management rather than redux.
1- Learning curve is very easy.
2- Easy to implement
3- Does not require installing additional library

But I definitely agree that, you should consider for what is the requirement of your app

Thank you for the article.

Collapse
arnelenero profile image
Arnel Enero • Edited

With the plethora of state management solutions for React, it doesn't have to be a strict dichotomy between Context vs. Redux. Some people wouldn't even regard Context as "state management", but rather just a "prop propagator". There are other good options for every requirement / situation / preference.

In fact, if you need something with a bit more than setter/getter i.e. to separate business logic from your components, there's a very simple approach: github.com/arnelenero/simpler-state (only 2 steps!). Sorry for that shameless plug, but what I'm trying to say is, our choice is not limited between Redux vs. Context.

Great article on the use of Context!

Collapse
jhayiwg profile image
Jhay Tabunan

Wow this is what I was looking as a solution before and just implemented few days ago, I hope I read this before because I spent a lot of time researching and try and error. I couldn't find a reference and this prove the concept is ok thanks.

Collapse
tuvudu profile image
Tuyen Vd.

Nice! in my project, I using context instead of redux, and it's still good so far.

Collapse
ajaymarathe profile image
Ajay Marathe

So helpful Tutorial Thanks

Collapse
aitweet01 profile image
©️hukwue🅱️uk🅰️

Great article. Thanks for sharing

Collapse
eleloi profile image
eleloi

Nice article, thank you for the explanation

Collapse
itshamhere profile image
Hamza Husein

In profile.js, shouldnt the props being called using useContext be destructed rather than passed in [] brackets?

Else, easy and simple to understand and implement! Thankyou🔥🔥

Collapse
bahlulhasanli profile image
Bahlul Hasanli

Thank! I think it's better to use useReducer together with useContext

Collapse
emptydn1 profile image
emptydn1

can i have your source code, pls

Collapse
ms_yogii profile image
Yogini Bende Author

It is already added in article!