In a usual React application, the common way of sharing data between two components is via prop drilling, i.e. passing the data as props from parent component to child component.

The pop drilling method is suitable for a small application with two or three nested child components. However, for complicated applications, the data must be passed down as props to each of the levels until it reaches the desired component. This requires additional code. As the prop is updated in a child component, it must be passed to all the components that require it. Because of this, components that don’t need the data need to be 'aware' of it.

A way around this issue is to provide a global state that all components, regardless of their nested position, could access. This can be achieved using Redux.

With the release of React version 16.3, the new Context API was introduced as a solution to React prop drilling. As stated in React Documentation:

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

With the introduction of Hooks with React version 16.8, Context API became even more popular and easy to use.

Getting started

The example below is based on a Counter component for a Sitecore website. The Counter has an initial value of 0 and two buttons to increase and decrease its value.

Redux counter example

Inside the main parent Sitecore Counter component, there are three child components; one for updating the counter and two for each of the buttons. The initial setup would be equal for both methods.

Redux parent component

Below examines each component in its respective method using the Redux approach and the Context API approach.

The Redux approach

Redux requires the following blocks to function:

  • Actions
  • Reducers
  • Store

Actions

Actions are the only source of information for the Store. Actions carry the information that sends data from the application to the Store. Below is an example of Action creators that dispatch an action:

Redux action

Reducers

Reducers are responsible for how the application changes in response to an action made by an end-user. Below is an example of a Reducer with a switch case that would return the new state based on the action:

Redux Reducer example

Store

The Store knits every thing together to make the application work. Below is an example of Store configuration:

Redux Store example

To make the Store accessible in the counter app and its children components, it needs to be wrapped inside the <Provider> component.

Context API Provider wrapper

To access the state and dispatch within Redux, use useSelector() and useDispatch() hooks, which were introduced by React-Redux with version 7.1 after React introduced hooks.

React redux hooks

Context API approach

Declaring a Reducer using the Context API approach is the same as Redux.

Global reducer

Other functions in this process include:

  • Create the context
  • Provide the context
  • Consume the context

Create the context

  1. Create a context using createContext() and pass the initial state as arguments. Context can also be defined without passing any arguments.
  2. Define a function that will deliver the data through the Provider.
  3. Using useReducer() hook accepts a Reducer with the default state, then returns the updated state and dispatches a function.
  4. Inside the provider function, use useReducer() and pass the Reducer and the initial state as arguments. The state returned and dispatch are then passed as values in the Provider.
Context API create

Provide the context

Now that context has been Created, context needs to be Provided in order to Consume it and make it accessible in the Counter its child components. This is achieved by wrapping it inside the Provider component.

Context API Provide

Consume the context

Use useContext() to consume the context in the child components.

Context API consume

Example of Redux and Context API Counter

Redux Context example

Redux vs Context API: Comparisons

Implementation

Context API is easy to is use as it has a short learning curve. It requires less code, and because there's no need of extra libraries, bundle sizes are reduced. Redux on the other hand requires adding more libraries to the application bundle. The syntax is complex and extensive creating unnecessary work and complexity. However, it's still a great alternative regarding prop drilling.

Rendering

Context API prompts a re-render on each update of the state and re-renders all components regardless. Redux however, only re-renders the updated components. This can be monitored on the console as there's a log in each component.

Redux context example 2

Redux vs Context API: Conclusions

  • Context API: Resourceful and ideal for small applications where state changes are minimal
  • Redux: Perfect for larger applications where there are high-frequency state updates

At Codehouse our expert development team is constantly exploring new technologies that will improve the development process and also the end user's digital experience. To find out more about how we help with your Sitecore project, get in touch.