Create a Global State with React Context 🦾, No Redux, No third party library at all, pure React for Global State.

React Context for global state with pure React

Gaurav Sharma's photo
Gaurav Sharma
·Aug 7, 2021·

7 min read

Create a Global State with React Context 🦾, No Redux, No third party library at all, pure React for Global State.

Subscribe to our newsletter and never miss any upcoming articles

Play this article

What is React Context?

A typical React application consists of many Components, things get pretty complex as an application grows. If you are using props to pass state values from your parent component to the child component, things will become more complex pretty soon, and you will end up passing props to infinite levels down in your component tree, which makes debugging really complicated.

Then Redux or any other state management library enter into the play, but no matter how good are those, it is just another library to handle along with your pure React application. Considering that, React introduced Context API with React 16. Let's see what it is capable of.

React Context and Redux:

Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.

This is what React official docs have to say about React Context, Before we start talking about some code, let's have a look at what Redux provides that we can replicate in our application with React Context.

redux-architecture.jpg

As you can see in the above picture, we have actions, reducers, and store to manage our state in Redux. You will not believe me, but we can achieve the same functionality with React Context as you move forward in this article. Let's break down the above image.

Actions - These are just functions, we call an action whenever we want to make changes in our application data, most of the time, these functions are simple API calls, which are capable to change the global state through dispatch.

Reducer - Aah! its name is really confusing for beginners, but no worries, these are also simple functions, which would take the old state and action as an argument and return the new state as an object.

Store - A store is an immutable object tree in Redux. You can think of it as a state container that holds the application's state.

Now we have got the idea of actions, reducer, and store, let's jump into the coding.

Let's create a StoreProvider HOC with React Context:

  1. First We will Start a simple React App with the help of create-react-app, run the command given below.

    $ npx create-react-app context-demo
    
  2. Just create a folder inside src/ with the name store.

  3. Create a file named index.js inside the src/store/ folder.

  4. Create a file named reducer.js inside the src/store/ folder.

  5. Let's write our StoreProvider HOC inside the src/store/index.js file.

     import React from "react";
     import { reducer } from "./reducer";
    
     // Create a initial state object
     const initialState = {
         "name": "Gaurav",
     }
    
     // Create a context with initial state
     export const Context = React.createContext(initialState);
    
     // State Provider to wrap the whole application
     const StoreProvider = (props) => {
         const [state, dispatch] = React.useReducer(reducer, initialState);
         const { children } = props;
    
         return (
             <Context.Provider value={[state, dispatch]}>{children}</Context.Provider>
         );
     };
    
     // Default export the StoreProvider
     export default StoreProvider;
    

    Explanation: First we have to create an initial state object to initialize the global state and context, you can keep it empty also. Then we need to create a Context using the initial state object. Once context creation is done, you can create your own StoreProvider component to wrap the whole application inside this provider and make your global state accessible everywhere in your application.

    You have also noticed that React.useReducer is using a reducer and initialState to initialize the [state, dispatch]. We have not created the reducer yet, let's jump into the next section to create a reducer.

Create a Reducer to utilize the useReducer hook:

Let's start with the reducer, we are aware of how reducer works. Let's create one for our demo application. Just open the reducer.js that we have created in the last section and write this code.

export const reducer = (state, action) => {
    // Check the type of action using switch-case
    switch (action.type) {
        // if action is 'CHANGE_MY_NAME'
        case 'CHANGE_MY_NAME':
            // return new immutable state object
            return {
                ...state,
                name: action.payload.name,
            };
        // default
        default:
            return state;
    }
};

Explanation: This is a simple reducer, which is taking state and action as an argument and checking the type of action which has been dispatched then returning a new state object accordingly.

Integrate the StoreProvider with our Application:

Now that we have created our StoreProvider HOC and a reducer, it is time to use these things in our application do some cool stuff with them.

  1. Open your src/index.js file.

  2. Import the StoreProvider component in this file.

     import StoreProvider from "./store";
    
  3. Now just wrap your App inside this StoreProvider like this.

     <StoreProvider>
         <App />
     </StoreProvider>
    

Now we are all set up and ready to use the state and modify the state. Let's do some more cool stuff.

Accessing the global state inside our components tree:

We can access the global state object in any of our components using the context that we have created, but we will do it in a much easier way by creating our own custom hook, Let's do it.

  1. Open your src/store/index.js file again.

  2. Write this code just above the StoreProvider component and below the context creation code.

     export const useStore = () => {
        // Below line will return -> [state, dispatch]
        return React.useContext(Context);
     };
    

    Explanation: We are creating a custom hook named useStore, which will return the current state and dispatch function to make state modifications through reducers.

    Let's see how it works in App.js component.

  3. Open your src/App.js file and erase everything in this file, and write this code.

     import React from 'react';
     import { useStore } from "./store";
    
     function App() {
         const [state, dispatch] = useStore();
         return <h1>{state.name}</h1>
     }
    
     export default App;
    

    Explanation: The initialState object that we had created earlier in this article is being used in this code. {state.name} is coming directly from the global state. Hope you have got the idea of a global state. Similarly, you can use the global state from anywhere in your application.

But the trickier part is to make the modification in our global state through actions as we do in redux. But we have got that too, in the above code, you can see that useStore hook is also returning the dispatch function, which we will use to make the modification in our global state through reducers let's do it.

Modifying the global state inside our components tree:

Let's rewrite our App.js component with some new functionality.

import React from 'react';
import { useStore } from "./store";

function App() {
    const [state, dispatch] = useStore();

    const handleChange = (event) => {
        dispatch({
          type: 'CHANGE_MY_NAME',
          payload: {
            name: event.target.value
          }
        })
    }

    return (
        <div>
            <input type="text" name="name" onChange={handleChange}/>
            <h1>{state.name}</h1>
        </div>
    )
}

export default App;

Explanation: Now that you see this is the simplest use of dispatch to modify our global state. The input element is connected to h1 element value indirectly through React Context and global state. In simple words, as you type in the input element, the value of global state will change using dispatch

Conclusion:

React is great, React Context is also great, but when it comes to debugging the Context, that's the toughest part of it. If you are planning to use context in a large-scale React application, then Redux will be a much smarter choice because of the features that you will get with it.

You can use context whatever the way you want it to, you can also create some actions to perform an `API call, I am leaving this all up to you, it depends on how creative you are with your React skills

Thanks for making it to the End of this article. If you found any mistakes in this article, please do inform me, I would appreciate it.

That's it for today in this blog, if you are finding any difficulty following this blog, you can find the complete code example in this codesandbox, the link is given below.


For more such crispy blogs daily, follow DevJunction, subscribe to our newsletter and get notified.

Did you find this article valuable?

Support Dev.Junction by becoming a sponsor. Any amount is appreciated!

See recent sponsors Learn more about Hashnode Sponsors
 
Share this