Hooks have been around for some time, but there is currently no official solution for state management. Therefore, hooks currently have three main solutions in the community to address application state management

  1. Use the redux-React-hook library instead of redux-Redux to work with Redux
  2. Use pure hook functions such as useReducer and useContext to replace react-redux and work with redux
  3. Use useReducer, useContext and other pure hook functions to completely replace react-redux and redux, and completely manage the application state through hooks

In my opinion, the most promising solution for the time being is the first one: Redux-React-hooks. Now in Facebook Incubator, which has a great chance to become an official solution, this session will focus on the first one.

As I’m sure you’ve noticed, either way, react-redux is replaced with hooks for state management. React-redux replaces React-Redux with react-Redux:

  • Maintain a store globally.
  • Any component can be retrieved from the Store, and preferably props can be customized (mapStatetoProps).
  • Provides the ability to dispatch actions (mapDispatchtoProps).

So if you want to understand why react-Redux was replaced? What state management pain points do hooks address? Why do hooks work better for state management? To understand these, we need to understand the following hooks first. In fact, redux-React-hook is also a package based on these hooks.

useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);
Copy the code

By using the useReducer hook, we can simulate part of the react-Redux feature, that is, the state is changed by the distributed action (triggering a dispatch operation), and the one-way data flow changes the store.

useContext

const value = useContext(MyContext);
Copy the code

The useContext hook is used to retrieve data and methods directly from the Context. In other words, it does not need to be nested and passed layer by layer in a component.

  • React. CreateContext Is used to initialize a Context.
  • The xxxContext. Provider receives a prop named Value as the top-level component, and can accept any string, number, or even function that needs to be put into the Context.
  • XXXContext.Consumer can appear anywhere in the component tree as the target component, receiving children prop, where children must be a function (context => ()) to receive the context from the top level.

We can use the useContext hook to solve the global state problem.

Having said that, let’s take a look at a small example to outline the function of these two hooks

Here is an example of a counter. You can click here to see this example

import React, { useState, useReducer } from "react";
import "./App.css";

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case "reset":
      return initialState;
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      returnstate; }}function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, { count: initialCount });
  return (
    <div className="App">
      Count: {state.count}
      <button onClick={()= > dispatch({ type: "reset" })}>Reset</button>
      <button onClick={()= > dispatch({ type: "increment" })}>+</button>
      <button onClick={()= > dispatch({ type: "decrement" })}>-</button>
    </div>
  );
}

export default Counter;
Copy the code

At first glance, it seems that React uses Hook to use redux mechanism. The state is changed by the action distributed, and the data flow is one-way. However, hook does not let the state share, that is, each time the useReducer keeps the data independent. Take this example:


function CountWrapper() {
  return (
    <section>
      <Counter initialCount={1}/>
      <Counter initialCount={1}/>
    </setion>
  )
}
Copy the code

We also need to solve the problem of sharing state between components. To solve the problem of global state, we can refer to the method of react-redux, providing a Provider and using context to do so. Here you can use useContext, the built-in hook.

It accepts a context object returned by react.createcontext, and useContext can return the latest value when the provider updates, which in this article is interpreted as an incoming store update.

import {createContext, useContext} from 'react';

const context = createContext(null);
export const StoreProvider = context.provider;

const store = useContext(context);
Copy the code

Next, if you want to simulate react-Redux completely, you need to customize a hook named useDispatch, exposing a hook to return the dispatch on the store to issue action, so as to change the state. You also need to create a custom name called useStoreState to get the global state by calling store.getStore(), looking at the component fetching data from the store

Although the state is taken, it ignores a very important issue, how to notify the component to fetch new data again when the data on the store changes, when the store changes, it is not associated with the view, and other issues…… Using pure hooks does solve the problem of state management, since space and time are no longer shared in detail, but they are too cumbersome and require a lot of custom hook functions to be written. If we need to use them in a project, we can use a high-level wrapper around these hook methods, the first of which is redux-React-hooks

Redux-React-Hooks

Redux-react-hook: Redux-react-hook: Redux-react-hook: Redux-react-hook: Redux-react-hook: Redux-react-hook

Within store.js, it is very simple to create a new Redux store using createStore, and any changes to the state must be made by reducer.

import {createStore} from 'redux';
import reducer from './reducers';

export const store  = createStore(reducer);
Copy the code

Reducers.js is a familiar recipe

const initialState = {
    counter: 0
}

export default function reducer(state = initialState,action){
    switch(action.type){
        case "INCREMENT":
            return {counter: state.counter+1}
        case "DECREMENT":
            return {counter: state.counter- 1}
        default:
            returnstate; }}Copy the code

Comparison: the react – story

To connect react and redux using react-redux, use the following indexWithouthooks.js:

import * as React from "react";
import { Provider } from "react-redux";
import ReactDOM from "react-dom";
import { store } from "./store";
import Counter from "./CounterWithoutHooks.";

ReactDOM.render(
  <Provider store={store}>
    <Counter name="Sara" />
  </Provider>,
  document.getElementById("root")
);
Copy the code

Counterwithouthooks.js needs to do this:

import * as React from "react";
import "./styles.css";

import { connect } from "react-redux";

export function Counter(props) {
  const { counter, increment, decrement } = props;
  return (
    <div>
      <h1>
        You pressed it {counter} times
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
      </div>
    </div>
  );
}

const mapStateToProps = state= > ({
  counter: state.counter
});

const mapDispatchToProps = dispatch= > ({
  increment: (a)= > dispatch({ type: "INCREMENT" }),
  decrement: (a)= > dispatch({ type: "DECREMENT"})});export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter);
Copy the code

Contrast: the story – the React – Hooks

If redux-react-hooks are used, there are some differences in index.js:

import * as React from "react";
import { StoreContext } from "redux-react-hook";
import ReactDOM from "react-dom";
import { store } from "./store";
import Counter from "./Counter";

ReactDOM.render(
  <StoreContext.Provider value={store}>
    <Counter />
  </StoreContext.Provider>,
  document.getElementById("root")
);
Copy the code

Redux-react-hook exposes storeContext. Provider instead of react-redux Provider

The biggest change is in Counter. Js, because redux-react-hooks provide useMappedState and useDispatch, the code to connect Counter is much simpler.

import * as React from "react";
import "./styles.css";
import { useMappedState, useDispatch } from "redux-react-hook";

export default function Counter(props) {
  const counter = useMappedState(state= > state.counter);
  const dispatch = useDispatch();

  return (
    <div>
      <h1>
        You pressed it {counter} times
      </h1>
      <div>
        <button onClick={()= > dispatch({ type: "INCREMENT" })}>Increment</button>
        <button onClick={()= > dispatch({ type: "DECREMENT" })}>Decrement</button>
      </div>
    </div>
  );
}
Copy the code

A useMappedState, which acts as mapStateToProps, uses useDispatch, and can be used directly within components without any special functions. One of the more obvious benefits is that the state tree and dispatch functions encapsulated by React-Redux no longer need to be passed through props, but defined and called directly from within the component itself, which simplifies the code load considerably

conclusion

Redux-react-hooks: Redux-hooks: Redux-hooks: Redux-hooks: Redux-hooks: Redux-hooks: Redux-hooks: Redux-hooks: Redux-hooks: Redux-hooks: Redux-hooks: Redux-hooks: Redux-hooks The essence of Hooks is close to functional programming thinking, which is consistent with Redux’s purely functional principles. Of course, as mentioned earlier, redux-React-hooks are not yet official, you can try other methods or libraries, but redux-Redux will most likely be replaced by hooks in the future, not just for state management, Using hooks is a better solution for scenarios like form processing, animation, subscription declarations, etc. Build applications using hooks. This is the future of React.