How to share state between multiple components without passing them in props (React, Context API).

John Doe
Active Developement
5 min readJul 2, 2021

This article will deal with a question you must have wondered about one day :

“ How can I interact with a state shared between multiple components, but without passing it down using the props of each components in between ? ”

Today, we will try to answer the question with an basic example. Improving upon the usual “todo list”, in this article we are going to create a simple “cookbook” app focused on this example.

Made from my ❤, don’t hesitate to hit me in private message for questions : https://www.linkedin.com/in/valentin-v-a0ba51153/

To quick start our example, I have made a repository containing two projects: one without usage of context API and the second with usage of context API.

I will start by presenting the project without the API context, which will allow us to raise the question. You can clone the repository with Git using the following commands:

git clone https://github.com/actived/cookbook-api-context.git
cd cookbook-api-context/01-without-context-api
npm install
npm start

Or by browsing the code with the sandbox below:

In our App.js the “recipes” state contains our recipes list and two functions to deal with it:

  • onAddRecipe: function in charge of adding a new recipe
  • onRemoveRecipe: function responsible for removing a previously created recipe (by identifier)
App.js

These functions will be used by components that want to modify our state.

So we have a component for adding new recipe “AddNewRecipeForm”, which will use “onAddRecipe” function passed in the props, to add a new recipe to the shared state when the user sends the form.

We will use this callback in the “onSubmit” function of the component:

RecipesList.jsx

There is a component named “RecipeCard” which display the details of a recipe and allow the removal of it via our function “onRemoveRecipe” passed in props that will modify our shared state.

RecipeCard.jsx

Now, lets imagine a component named “RecipesList” which only renders the recipe details, we will pass the shared state “recipes” in its props and also the function “onRemoveRecipe” which in turn will be passed down to the props of the component “RecipeCard” since it also needs to be able to remove a component.

RecipesList.jsx

At this point we realise that our “onRemoveRecipe” function is only used in our component “RecipeCard”. But the component “RecipesList” above it must have it too, since it has to pass it to its child component.

This brings us back to our starting question:

“ How can I interact with a state shared between multiple components, but without passing it down using the props of each components in between ?”

For this example a first answer could be : “By not having a component to just display a list” and yes it is a very good answer but here we are trying to show that for each levels of component you will have above the component using “onRemoveRecipe”, then you will need a prop to pass the callback down.

The correct answer regardless of the complexity is “By using the context API of React”. It will keep all the logic around the shared state “recipes”.

To do this I created a second project identical to this one but containing the changes made to use the context API.

If you have the repository, you just need the following commands:

cd cookbook-api-context/02-with-context-api
npm install
npm start

Otherwise look at the sandbox below:

To use the React context API in our example we create a new file named “RecipesContext.jsx” in the “contexts” folder to clearly separate the components from the contexts.

In this file we can find the creation of our context with the “createContext()” function of the React API which takes as an argument the content of the context but we will use it to describe the context instead.

So there is our state “recipes” and our two functions “addRecipe” (which previously was “onAddRecipe” in App.js) and “removeRecipe” (which previously was “onRemoveRecipe” in App.js)

There is also a component named “RecipeContextProvider” which will contain all the logic around the context created, to do this we wrap the “provider” of the context, in order to manage the business logic between the child components and our context.

RecipesContext.jsx

As you can see here we simply moved the code around the “recipes” state that was in App.js.

Now if we look at our “App” component we can see that it has been cleaned up:

App.js

It is important to understand that if you want components to interact with a context, you have to encompass them with its provider. In our case that is why we can see that the components “AddNewRecipeForm” and “RecipesList” are both wrapped by the component “RecipeContextProvider”.

Note that all child components of a component will be able to access our context: for our example, both “RecipesList” and “RecipeCard” will have access to the context.

Now to interact with our context it will be through the “useContext()” hook that we will reach our state and the add and delete functions. We no longer need to pass them as props!

RecipesList

We no longer need to pass our delete function between the components “RecipesList” and “RecipeCard”.

RecipeCard.jsx

The component “AddNewRecipeForm” is also simplified, no need to have the add function in the props.

AddNewRecipeForm.jsx

This is my very first article, I hope it will be useful to you! If you have any questions or find some areas unclear, do not hesitate to leave a message I will be happy to answer you.

Happy code to all !

--

--