As we all know, React’s one-way data flow mode results in state being transferred from parent to child level by level, which is cumbersome and difficult to manage in medium and large applications. We usually need to use Redux to help manage it. However, with the release of React 16.3, the new Context API becomes a new option.

I. Introduction and defects of Redux

Redux comes from Flux and borrows from the idea of Elm. Its main principles are as follows:

It can be seen that the data flow of Redux is actually very simple. The external event invokes Dipsatch to release the action into reducers through actionCreator function. The reducer then updates the state of the entire application as needed based on the action type (action.type).

The redux design has the following elements:

1. State is singleton mode and immutable. Singleton mode avoids the complexity of data exchange between different stores, while immutable data provides very fast undo redo, “time travel” and other functions.

2. State can only be updated by reducer and cannot be directly modified.

Reducer must be a pure function, such as (state,action) => newState

Redux is a very pure state management library. You need the help of react-Redux to manage react state. React-redux consists of two main parts.

1. Provider component: Stores can be injected into coText of child components, so they are usually placed at the top of the application.

2. Connect function: returns a higher-order function that takes the store injected by the Provider from the context and passes it to the child component via props, so that the component can access the Store.

Although Redux is widely recognized and used in React projects, it still has many disadvantages in real projects:

1. Excessive boilerplate code: Adding an action usually requires defining corresponding actionType and then writing N related reducer. For example, when an asynchronous loading event is added, three actionTypes including loading, loading failure and loading completion need to be defined at the same time, and a reducer corresponding to the reducer needs to process the corresponding actiontypes through the switch branch, which has too much redundant code.

2. Update efficiency problem: Due to immutable data mode, a complete copy of state is required every time state is updated, resulting in memory waste and performance loss.

3. Data transfer efficiency problem: Due to the old context API used by React-Redux, the context transfer efficiency problem exists.

Among them, there are a lot of solutions to the first problem, such as DVA, Rematch and mirror. The author also made a similar wheel restated and I won’t elaborate too much here.

Redux and React-Redux have been optimized in detail. ShouldComponentUpdate is a good way to avoid unnecessary updates. You can also use immutable data structures such as IMMUTABLE and Immr to fundamentally solve the copy overhead problem.

The third problem is the limitations of the React API. From the perspective of third-party libraries, there is very little that can be done.

Second, the Context API

The context API is used to solve the problem of cross-component parameter transfer. The syntax of the old context API is as follows:


As you can see, we can use the Context API to pass the color parameter from DeliverComponent directly across the MidComponent to ReceiverComponent without using the props parameter. Especially if the ReceiverComponent hierarchy is very deep, using the Context API can greatly save repetitive code and avoid bugs.

Defects of the old Context API

The old Context API has the following major drawbacks:

1. Code redundancy: The component providing the context must define ‘childContextTypes’ and’ getChildContext ‘to pass the context. The receiver of the context also needs to define contextTypes to get the data correctly.

2. Transmission efficiency: Although the context can be transmitted across layers functionally, in essence, the context is also transmitted layer by layer as props. When the layer is too deep, efficiency problems still occur.

ShouldComponentUpdate: Since the context is also passed layer by layer, it should also be blocked by the shouldComponent. In other words, if one of the intermediate components’ shouldComponentUpdate method returns false when the context of the passing component changes, the subsequent receiving component will not receive any context changes.

ShouldComponentUpdate (shouldComponentUpdate) {shouldComponentUpdate (shouldComponentUpdate) {shouldComponentUpdate (shouldComponentUpdate) {shouldComponentUpdate (shouldComponentUpdate) {shouldComponentUpdate (shouldComponentUpdate); Subsequent components will not receive state changes), and each CONNECT component needs to listen for state changes, either directly or indirectly, when state changes, The internal notifyNestedSubs method triggers each child component from top to bottom to get the latest state update view through getState method. This approach is inefficient and hack.

New Context API

React provides a new context API starting in 16.3, which completely resolves the problems of the old context API.

Here’s how the new Context API (right) compares to the React-Redux data stream (left) using the old context API:

As you can see, the new Context API can pass context data directly to and from child components instead of cascading like the old Context API. ShouldComponentUpdate can also be broken. The new context API is defined as follows:


Here is a simple application example:


You can see that the new Context API mainly contains a Provider and Consumer pair, and the data entered in the Provider is available in the Consumer. The main points of the new Context API are as follows:

1. Provider and Consumer must come from the same React. CreateContext call. Namecontext. Provider and agecontext. Consumer cannot be used together.

2. The React. CreateContext method accepts a default value as an argument. This default is used when there is no corresponding Provider in the Consumer outer layer.

3. When the valueProp value of the Provider component changes, the corresponding Consumer component in its internal component tree receives the new value and re-executes the children function. This process is not affected by the shouldComponentUpdete method.

4. The Provider component uses Object.is to check whether the value of Value Prop is updated. Note that object. is and === do not behave exactly the same.

5. The pattern in which the Consumer component receives a function as a children prop and generates a tree of components using the return value of that function is called the Render Props pattern.

4. Apply the new Context API

The new Context API greatly simplifies the react state transfer problem, and there are also some state management libraries based on it, such as: Unstated, react-waterfall, etc. Next we’ll try to build a React-Redux wheel using the new Context API.

1. Provider

Since the new context API is not blocked by shouldComponentUpdate, we just need to listen for store changes in the Provider:


2. connect

In contrast to react-Redux, the higher-order component logic in Connect is much simpler. It does not need to listen for store changes, but obtains the state passed in by the Provider and passes it to its child components:


Now that the entire React-Redux interface and functionality are covered, let’s continue with some important performance optimizations.

3. Performance optimization – Reduce repeated rendering

One of the biggest parts of performance optimization is to reduce unnecessary re-rendering. We should prevent WrappedComponent from re-rendering when the WrappedComponent parameter values have not changed. ShouldComponentUpdate or PureComponent directly to achieve our goal:


It is important to note that the mapDispatchToProps parameter does not support the ownProps parameter, so its return value can be treated as immutable. Otherwise, the action function is created every time it is called, resulting in Prevent receiving different parameters. For more complex scenarios, see the Selector section of the React-Redux source code.

4. Performance optimization – Reduce level nesting

Another important aspect of performance optimization is to reduce the level of nesting of components. The new Context API has a disadvantage over the old Context API, which requires a layer of nested Consumer components to fetch context values. In addition, we should minimize the nesting of levels. So instead of nesting a PureComponent in the previous optimization, we can implement a memory mechanism directly in Cunsumer as follows:

The following is a comparison of the component levels in chrome developer tools before and after. You can see that the nesting level is successfully reduced by one layer. Two layers of nesting is a limitation of the new Context API, which cannot be simplified if you want to maintain the React-Redux interface mode.