DEV Community

Jason Mei
Jason Mei

Posted on

How I Set Up Context in React

The Context API is a feature in React that allows your application to have app-level state without the use of a state management library like Redux. It's a neat feature that I highly recommend everyone try out if you're going to be working with React. Setting it up however, can be a bit confusing for beginners. Here, I'm going to outline how I go about setting up Context. This is by no means the "best way" of doing things, so if you have a more efficient way of implementing Context, I'd love to hear it.

First, create a new React app using the npx create-react-app command and open up the new project in your code editor. If you did, your file structure should look something like this:
Screen Shot 2020-09-28 at 5.53.25 PM

Setting Up Context

Step 1: Create a "context" folder in the "src/" directory.
Step 2: Create a "type.js" file in the context folder - this will hold your reducer action types.

// src/context/types.js
export const ADD_CONTACT = "ADD_CONTACT"; // this will eventually be passed to the reducer

Step 3: In the "context" folder, create a folder and name it after the state you will be managing. In this example, I'm making an app that keeps track of a user's contacts, so I named this folder "contact".

Step 4: In the "contact" folder, create 3 files for the Context, Reducer, and State. In this example, my files are contactContext, contactReducer, and ContactState:
Screen Shot 2020-09-28 at 6.13.51 PM

Step 5: In the contactContext file:

// src/context/contact/contactContext.js
import { createContext } from "react"; 

const contactContext = createContext(); // declare your context variable and set it to a new context using createContext()

export default contactContext;

Here we simply initialize a new context and export it. This will be imported into our ContactState.

Step 6: In the contactReducer file:

// src/context/contact/contactReducer.js
import {
  ADD_CONTACT
} from "../types"; // import the action types you will be using in this reducer

// export your switch case statement to handle the actions dispatched to the reducer
export default (state, action) => {
  switch (action.type) {
    case ADD_CONTACT:
      return {
        ...state,
        contacts: [...state.contacts, action.payload],
      };
    default:
      return state;
  }
};

Step 7: In the ContactState file:

// src/context/contact/ContactState.js
import React, { useReducer } from "react"; // import useReducer hook
import { v4 as uuid } from "uuid"; // using uuid to create random ID for a new contact

// import contactContext and contactReducer
import ContactContext from "./contactContext"; 
import ContactReducer from "./contactReducer";

// import types from types.js to be dispatched to ContactReducer vis the useReducer hook
import {
  ADD_CONTACT
} from "../types";

const ContactState = (props) => {
  const initialState = {
    contacts: [
      {
        id: 1,
        name: "John Doe",
        email: "john@gmail.com",
        phone: "111-111-1111",
      }
  };

// pass ContactReducer and initial state to useReducer hook in order to mutate app-level state
  const [state, dispatch] = useReducer(ContactReducer, initialState); 

  // Add Contact
  const addContact = (contact) => {
    contact.id = uuid();
    dispatch({ type: ADD_CONTACT, payload: contact });
  };

  return (
    {/* Return the Context Provider with the value prop set as an object of the state and props we want all components to have access to  */}
    <ContactContext.Provider
      value={{
        contacts: state.contacts, {/* passing down contact state*/}
        addContact {/* passing down a function*/}
      }}
    >
      {props.children}
    </ContactContext.Provider>
  );
};

export default ContactState;

Step 8: Finally, simply import the ContactState into your App.js and wrap your entire app in the Provider:

// src/App.js
import React from 'react';
import Contacts from './components/Contacts';
import './App.css';

import ContactState from "./context/contact/ContactState";

const App = () => {
  return (
    {/* Wrap entire app in ContactState, which returns the Provider. This will allow all components in the app to have access to the state in ContactState */}
    <ContactState>
      <Contacts />
    </ContactState>
  );
}

export default App;

By wrapping the entire App in ContactState, all the components of App become children of ContactState and can now access the state via the useContext hook.

//src/components/Contacts.js
import React, { useContext } from "react"; // importing useContext hook
import ContactContext from "../../context/contact/contactContext";

const Contacts = () => {
  // declare a context variable with the useContext hook and now the Context component has access to the state from ContactContext
  const contactContext = useContext(ContactContext);

  // destructure out contacts from our contactContext
  const { contacts } = contactContext;

  return (
    <div>
      {contacts.map(contact => <h1>{contact.name}</h1>)}
    </div>
  );
};

export default Contacts;

Top comments (0)