DEV Community

Cover image for useContext for better state management!
Yogini Bende
Yogini Bende

Posted on • Edited on

useContext for better state management!

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. You can also connect with me on Twitter or buy me a coffee if you like my articles.

Keep learning 🙌

Top comments (33)

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
 
veercodeprog profile image
Veer Pratap

does that mean we should avoid using useContext.

Collapse
 
luubinhan profile image
Lưu Bình An

I think there some use case that a good fit for useContext, for example you have some data that readonly between multiple components.

Thread Thread
 
veercodeprog profile image
Veer Pratap

I want to use it for sessionmanger to pass user object in body tag.

Thread Thread
 
veercodeprog profile image
Veer Pratap

stackoverflow.com/questions/763610...
Can you help me out with this

Collapse
 
frankgo81 profile image
FRANK GO

agree. for complex projects with complex frontend UX it is difficult and in-efficient for context

Collapse
 
ratneshmurugesan profile image
Ratnesh Murugesan • Edited

Which alternate concept are you preferring / using nowadays?

Collapse
 
luubinhan profile image
Lưu Bình An
Thread Thread
 
zaghadon profile image
David Nzagha

This is literally the best stuff I saw here, gracias 🙌

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
 
hey_yogini profile image
Yogini Bende

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
 
liunate profile image
Nate Liu

To me redux(as state mgmt) and context(as dependency injection) are complementary, so they could be both used in a fairly complex app. Redux at high above and context at finer granular part of page.

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
 
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
 
hey_yogini profile image
Yogini Bende

Thank you 🙌

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
 
aslemammad profile image
Mohammad Bagher Abiyat

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
 
ianwijma profile image
Ian Wijma

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

Collapse
 
tuvudu profile image
Tuyen Vd.

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

Collapse
 
jazz568 profile image
jazz568

Simple explanation. Thank you.
It would be nice to further explain that, when using multiple sets of data in useContext, how the React is re-rendering based on the change(s) of these sets of data? e.g. Is it re-rendering everything under the provider umbrella? or is it just re-rendering the components using the changed state data? This is always very confusing to me.