bene : studio is a global consultancy, helping startups, enterprises and HealthTech companies to have better product
What is the use of context API in React?
At bene : studio, we love knowledge sharing to help the community of professionals. To share our 10+ years of experience we have launched a knowledge hub on our blog with regular updates of tutorials, best practices, and open source solutions.
These materials come from our internal workshops with our team of developers and engineers.
Pardon the interruption, we have an important message!
We are looking to expand our team with talented developers. Check out our open positions and apply.
Introduction
Context is a built-in API introduced in React 16.3. It makes it possible to pass data from parent to children nested deep down the component tree directly, instead of passing it down through a chain of props. It can be used to solve the same problems as Redux does, but it doesn’t provide a strict way of state manipulation to ensure predictable behavior. Despite its low-level nature, it might be the perfect solution in some cases.
Overview of the API
Creating a context
A context contains references to variables and notifies its consumers when these references are changed. There are no restrictions on what type of values you can provide, so you can easily pass in functions that can be used to communicate events back from consumers, for example.
To create a context, call the createContext function with an arbitrary value. This will be the default value provided to the consumers. Optionally provide an interface describing your provided object. The returned object will contain helper components you need to provide or consume this context.
Default values are only used when you try to access context in a component where no provider is present above it in the component tree. I will go into more detail in a bit about how scoping works.
Provider
The context object we just created exposes a Provider component that has a prop named value. Any primitive value or object passed to this prop will be accessible in any child of this component.
Consuming
There are three ways to access the provided data: Consumer component, useContext hook, and contextType.
Consumer component
The context object also exposes a Consumer component that calls its child function with the current value. See: Render Props
useContext hook
With the useContext hook, the context object is used as an injection token to access its value. If the value is an object, it can be easily deconstructed to separate constants. Please note useContext can only be used in function components, and the same rules apply as with other hooks.
contextType
A single context object can be assigned to a class component’s contextType property and accessed as this.context.
Scoping
In the example below there are multiple providers with different values. The consumer component will use the first value provided above it in the tree. This means it is quite easy to override values for some parts of the application. The third consumer is not a child of any of the providers so it will use the context’s default value.
Complex example
It is a good idea to encapsulate the provided values and logic in one place. This can be achieved by creating a wrapper provider component, that will contain the state and provide it for its children.
This way you can have multiple provider components containing state and logic to make them be accessible from everywhere in your application, almost like services. These can be packaged neatly in a Providers component. Be aware, a provider’s values are only accessible to its children, so in your providers, you can only use other contexts if they are provided above it.
With the help of Contexts, Custom hooks can encapsulate logic that is dependent on the global state. For example, if you have translated options for select components, you can’t simply extract the options into a separate file because it needs to know the currently selected language. However, with a useOptions custom hook, you can use these options in multiple places in your app.
When to use the context API
The Context API gives you a dependency injection system based on your component tree. This wasn’t possible in React before and it also integrates very well with function components and hooks. As we saw above it was easy to implement encapsulated logic for the translated options with react-intl, which also uses context internally. In situations where you have logic and configuration that needs to be accessed by multiple components but doesn’t interact with or depend on external modules, writing your own contexts can be beneficial.
A good example would be a UI library that needs to function independently from the applications using it but still needs a better way of sharing data than passing everything through props. In a UI library, the tree-based scoping is very useful because, for example, you can override the theme of your components in just a part of your application. We can also imagine a calendar component with a ton of components and logic. In this case, we can build multiple contexts that help to manage all of its internal events and configurations and they won’t be affected by any other instance of this widget.
Compared to Redux
Redux on the other hand gives you a solution to manage your application state in a centralized location and in a standardized way. In fact, React Redux internally uses context to access the global store with the useSelector and useDispatch hooks. React includes the useReducer hook, which lets you manage the components state with reducers, but it lacks a lot of features that would make it easy to manage a whole applications state with it.
Redux gives you the ability to combine reducers, apply middlewares, make async operations with the help of thunks or saga, reload application state from local storage, and a powerful debugging extension. Replicating these features with useReducer wouldn’t make a lot of sense in my opinion.
Conclusion
If you are in a situation where you need to implement some sort of library or widget system, then building on top of the Context API will give you most of what you need. And you also get the benefit of reduced bundle size, because it’s already built into React. And this is not limited to libraries that you plan to publish. Maintaining internal tooling in a large application will benefit you in the long run.
However, Redux is still a more complete solution for managing application state and business logic.
Do you have questions?
Please send them to partner@benestudio.co, and we are happy to set up a talk with our engineers.
Are you looking for a partner for your next project? Check out our services page to see what we do, and let’s set up a free consultation.