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:
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:
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)