
A new Context API has been added to React 16.3.
New in the sense that the
old Context API was off-screen, most people either did not know about its existence, or did not use it, because the documentation advised to avoid using it.
However, now the Context API is a full-fledged part of React, open for use (not as it used to be, officially).
Immediately after the release of React 16.3, there were articles that proclaimed the death of Redux due to the new Context API. If you asked Redux about this, I think he would answer - “the reports of my death are
greatly exaggerated .”
')
In this post I want to talk about how the new Context API works, how it looks like Redux, when you can use Context instead of Redux, and why Context does not replace Redux in each case.
If you just need an overview of the Context API, you can follow the link .Sample React Application
I'm going to assume that you have an understanding of how to work with a state in React (props & state), but if not, I have a free 5-day course that will help
you learn about it .
Let's look at an example that will lead us to the concept used in Redux. We'll start with a simple version of React, and then see how it looks in Redux and, finally, with Context.

In this application, user information is displayed in two places: in the navigation bar in the upper right corner and in the sidebar next to the main content.
(You may notice that there is a great similarity with Twitter. Not by chance! One of the best ways to hone your React skills is to
copy (create replicas of existing sites / applications) .
The structure of the component looks like this:

Using pure React (props only) we need to store user information high enough in the tree so that it can be passed on to the components that need it. In this case, user information must be in the App.
Then, in order to pass information about the user to the components that need it, the application must pass it to Nav and Body. They, in turn, will pass it to UserAvatar (hurray!) And Sidebar. Finally, Sidebar should pass it on to UserStats.
Let's take a look at how this works in the code (I put everything in one file to make it easier to read, but in fact it will probably be divided into separate files, following some kind of
standard structure ).
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; const UserAvatar = ({ user, size }) => ( <img className={`user-avatar ${size || ""}`} alt="user avatar" src={user.avatar} /> ); const UserStats = ({ user }) => ( <div className="user-stats"> <div> <UserAvatar user={user} /> {user.name} </div> <div className="stats"> <div>{user.followers} Followers</div> <div>Following {user.following}</div> </div> </div> ); const Nav = ({ user }) => ( <div className="nav"> <UserAvatar user={user} size="small" /> </div> ); const Content = () => <div className="content">main content here</div>; const Sidebar = ({ user }) => ( <div className="sidebar"> <UserStats user={user} /> </div> ); const Body = ({ user }) => ( <div className="body"> <Sidebar user={user} /> <Content user={user} /> </div> ); class App extends React.Component { state = { user: { avatar: "https://www.gravatar.com/avatar/5c3dd2d257ff0e14dbd2583485dbd44b", name: "Dave", followers: 1234, following: 123 } }; render() { const { user } = this.state; return ( <div className="app"> <Nav user={user} /> <Body user={user} /> </div> ); } } ReactDOM.render(<App />, document.querySelector("#root"));
Sample CodeSandbox CodeHere the App
initializes the state containing the “user” object. In a real application, you will most likely
retrieve this data from the server and save it in state for rendering.
As for props forwarding (“prop drilling”),
this is not a problem . It works fine. “Forwarding props” is the perfect example of React work. But forwarding to the depth of the state tree can be a little annoying when writing. And annoy more and more if you have to transfer a lot of props (and not just one).
Nevertheless, there is a big minus in this strategy: it creates a connection between the components that should not be connected. In the example above, Nav should accept the “user” prop and pass it to UserAvatar, even if Nav does not need it.
The closely related components (for example, those that transfer props to their children) are more difficult to reuse, since you must attach them to new parents whenever you use them in a new place.
Let's see how we can improve this.
Before using Context or Redux ...
If you can find a way to
unify the structure of your application and take advantage of the transfer of props to descendants, this can make your code cleaner without having to resort to deep probros props,
Context , or
Redux .
In this example, children props is a great solution for components that should be universal, such as Nav, Sidebar and Body. Also know that you can pass JSX to
any prop, not just children - so if you need more than one “slot” for connecting components, keep this in mind.
Here is an example of a React application in which Nav, Body and Sidebar take child elements and display them as is. Thus, the person who uses the component does not need to worry about the transfer of certain data that is required by the component. He can simply display what he needs in place, using the data he already has in scope. This example also shows how to use any prop to transfer children.
(Thanks to Dan Abramov for
this offer !)
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; const UserAvatar = ({ user, size }) => ( <img className={`user-avatar ${size || ""}`} alt="user avatar" src={user.avatar} /> ); const UserStats = ({ user }) => ( <div className="user-stats"> <div> <UserAvatar user={user} /> {user.name} </div> <div className="stats"> <div>{user.followers} Followers</div> <div>Following {user.following}</div> </div> </div> ); // children . const Nav = ({ children }) => ( <div className="nav"> {children} </div> ); const Content = () => ( <div className="content">main content here</div> ); const Sidebar = ({ children }) => ( <div className="sidebar"> {children} </div> ); // Body sidebar content, , // . const Body = ({ sidebar, content }) => ( <div className="body"> <Sidebar>{sidebar}</Sidebar> {content} </div> ); class App extends React.Component { state = { user: { avatar: "https://www.gravatar.com/avatar/5c3dd2d257ff0e14dbd2583485dbd44b", name: "Dave", followers: 1234, following: 123 } }; render() { const { user } = this.state; return ( <div className="app"> <Nav> <UserAvatar user={user} size="small" /> </Nav> <Body sidebar={<UserStats user={user} />} content={<Content />} /> </div> ); } } ReactDOM.render(<App />, document.querySelector("#root"));
Sample CodeSandbox CodeIf your application is too complex (more complicated than this example!), It may be difficult to understand how to adapt the pattern for children. We'll see how you can replace props probros with Redux.
Redux example
I will quickly review an example on Redux so that we can better understand how Context works, so if you do not have a clear understanding of how Redux works, first read
my introduction to Redux (or watch the
video ).
Here is our React application, converted to use Redux. User information has been moved to the Redux store, which means that we can use the react-redux connect function to directly transfer user prop to the components that need them.
This is a big victory in terms of getting rid of connectedness. Take a look at Nav, Body and Sidebar, and you will see that they no longer accept or transfer user prop. No longer play hot potatoes with props. No more useless links.
Reducer does little here; it's pretty simple. I have something else about how
Redux reducers work and
how to write the immutable code that is used in them.
import React from "react"; import ReactDOM from "react-dom";
Sample CodeSandbox CodeNow you probably wonder how Redux achieves this magic. Amazing. How does React not support the transfer of props to several levels, and can Redux do this?
The answer is that Redux uses the
context function React (context feature). Not a modern Context API (not yet), but an old one. One that in the React documentation is told not to use if you are not writing your library or do not know what you are doing.
The context is similar to the computer bus following each component: to get the power (data) passing through it, you only need to connect. And react-redux connect does exactly that.
However, this Redux feature is only the tip of the iceberg. Transferring data to just the right place is the most
obvious of the features of Redux. Here are some other benefits you get out of the box:
connect is a pure functionconnect automatically makes the connected components "clean", that is, they will be re-rendered only when their props change - that is, when their Redux state slice changes. This prevents unnecessary re-rendering and speeds up the application.
Easy debugging with ReduxWriting out actions and reducers is balanced by the amazing debugging ease Redux provides you.
With
the Redux DevTools extension, you get an automatic log of all actions performed by your application. At any time, you can open it and see what actions were launched, what payload they had, and state before and after the action.

Another great feature that Redux DevTools provides is debugging with the help of
“time travel” , so you can click on any previous action, and go to this point in time, up to the current one. The reason this works is that each action updates the store in the
same way , so you can take a list of the recorded status updates and play them without any side effects, and finish in the right place.
There are also tools like
LogRocket , which basically give you permanent Redux DevTools in
production for each of your users. Got a bug report? No problem. View this user session in LogRocket, and you can see a repetition of what he did and which actions were launched. It all works using the Redux action stream.
Extending Redux with MiddlewareRedux supports the concept of middleware (a fancy word for "a function that runs every time an action is sent"). Writing your own middleware is not as difficult as it may seem, and allows you to use some powerful tools.
For example…
- Do you want to send an API request every time the action name starts with FETCH_? You can do this using middleware.
- Want a centralized place for logging events in your analytical software? Middleware is a good place for this.
- Want to prevent action from starting at a specific point in time? You can do this using middleware, invisible to the rest of your application.
- Want to intercept an action that has a JWT token, and automatically save it in localStorage? Yes, middleware.
Here is a good
article with examples of how to write redux middleware.
How to use React Context API
But maybe you do not need all these oddities of Redux. Perhaps you don’t need simple debugging, tuning, or automatic performance improvements — all you want to do is transfer data easily. Maybe your application is small, or you just need to quickly do something and deal with the subtleties later.
The new Context API will probably suit you. Let's see how it works.
I posted a quick
lesson on Context API on Egghead, if you like looking more than reading (3:43).
Here are 3 important components of the Context API:
- React.createContext function that creates context
- Provider (returns createContext), which sets the "electrical bus",
passing through the component tree - Consumer (also returns createContext), which is absorbed into
"Electrical bus" to extract data
Provider is very similar to Provider in React-Redux. It takes on a value that can be anything you want (it can even be a store Redux ... but that would be stupid). Most likely, this is an object containing your data and any actions that you want to perform with the data.
Consumer works a bit like the connect function in React-Redux, connecting to the data and making it available to the component that uses it.
Here are the highlights:
Sample CodeSandbox CodeLet's take a look at how this works.
Remember, we have 3 parts: the context itself (created using React.createContext) and two components that interact with it (Provider and Consumer).
Provider and Consumer work togetherProvider and Consumer are interconnected and inseparable. They only know how to interact with each other. If you create two separate contexts, say “Context1” and “Context2”, then Provider and Consumer Context1 will not be able to communicate with Provider and Consumer Context2.
Context does not contain stateNote that context
does not have its own state . This is just a feed for your data. You must pass the value to the Provider, and this value is passed to any Consumer that knows how to find it (the Provider is tied to the same context as the Consumer).
When you create a context, you can pass a “default value” as follows:
const Ctx = React.createContext(yourDefaultValue);
The default value is what the Consumer will receive when placed in a tree without Provider above it. If you do not pass it, the value will be undefined. Note that this
is the default value , not the
initial value. ontext does not save anything; it simply distributes the data you transmit to it.
Consumer uses Render Props patternThe connect Redux function is a higher order component (abbreviated as HoC). It wraps another component and passes props into it.
Consumer, by contrast, expects the child component to be a function. It then calls this function during rendering, passing the value it received from Provider somewhere above it (or the default value from the context, or undefined if you don’t pass the default value).
Provider takes one valueJust one value, like prop. But remember that the value can be anything. In practice, if you want to pass several values ​​down, you must create an object with all the values ​​and pass this object down.
Context API flexible
Since creating a context gives us two components to work with (Provider and Consumer), we can use them as we want. Here are a couple of ideas.
Wrap a Consumer in a HOCDon't like the idea of ​​adding a UserContext.Consumer around every place that needs it? This is your code! You have the right to decide what will be the best choice for you.
If you prefer to get the value as a prop, you can write a small wrapper around the Consumer as follows:
function withUser(Component) { return function ConnectedComponent(props) { return ( <UserContext.Consumer> {user => <Component {...props} user={user}/>} </UserContext.Consumer> ); } }
After that, you can rewrite, for example, UserAvatar using the withUser function:
const UserAvatar = withUser(({ size, user }) => ( <img className={`user-avatar ${size || ""}`} alt="user avatar" src={user.avatar} /> ));
And voila, context can work the same way as connect Redux. Minus automatic cleanliness.
Here is an example of
CodeSandbox with this HOC.
Keep State in ProviderRemember that Provider is just a channel. It does not save any data. But this does not prevent you from making your
own data wrapper.
In the example above, the data is stored in the App, so the only thing you need to understand is the components Provider + Consumer. But maybe you want to create your own store. You can create a component to store the state and pass them through the context:
class UserStore extends React.Component { state = { user: { avatar: "https://www.gravatar.com/avatar/5c3dd2d257ff0e14dbd2583485dbd44b", name: "Dave", followers: 1234, following: 123 } }; render() { return ( <UserContext.Provider value={this.state.user}> {this.props.children} </UserContext.Provider> ); } } // ... ... const App = () => ( <div className="app"> <Nav /> <Body /> </div> ); ReactDOM.render( <UserStore> <App /> </UserStore>, document.querySelector("#root") );
Now the user data is contained in its
own component, the only task of which is this data. Cool. The app can again become clean (stateless). I think it looks a bit cleaner.
Here is an example of
CodeSandbox with this UserStore.
Throw actions down through contextRemember that the object passed through the Provider may contain everything you want. This means that it may contain functions. You can even call them actions.
Here is a new example: a simple room with a switch to switch the background color - oh, I mean light.

State is stored in the store, which also has the function of switching light. Both state and function are passed through the context.
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css";
Here is a full working example in
CodeSandbox .
So after all, what to use, Context or Redux?Now that you've seen both ways - which one should you use? I know that you just want to hear the answer to this question, but I have to answer - "depends on you."
It depends on how big your application is now or how fast it will grow. How many people will work on it - just you or a big team? How experienced are you or your team in working with the functional concepts on which Redux relies (such as immunity and pure functions).
A disastrous mistake that permeates the entire JavaScript ecosystem is the idea of
competition . There is an idea that each choice is a zero-sum game: if you use
library A, you should not use its competitor
library B. What happens when a new library comes out, which is something better than the previous one, it should supplant the existing one. That everything should be either / or that you should either choose the newest and best, or be upstaged along with the developers from the past.
The best approach is to look at this wonderful choice with an example of a set of tools. This is like choosing between using a screwdriver or a powerful screwdriver. For 80% of cases, the screwdriver will do the job easier and faster than a screwdriver. But for the other 20% a screwdriver would be the best choice (little space, or a thin object). When I bought a screwdriver, I did not immediately throw out the screwdriver, he did not replace it, but simply gave me another option. Another way to solve the problem.
Context does not "replace" Redux, nothing more than React "replaced" Angular or jQuery. Hell, I still use jQuery when I need to do something quickly. I still occasionally use server-side EJS templates instead of deploying React. Sometimes React is more than you need to complete a task. The same goes for Redux.
Today, if Redux is more than you need, you can use context.
Learning React can be hard - so many libraries and tools!
My advice? Ignore them all :)
For step-by-step learning, read my book
Pure React .