This is an introduction to Redux, please check out the official documentation for more information.

In this article, we will implement a small example of a breathless counter in three ways. First, we will implement it with React, then we will introduce Redux to learn what Redux is, why Redux is used, and how to use it simply.

React implements the counter

Create a creact-react-app, add the bootstrap CDN to index.html, and change app.js to the following.

<link href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
Copy the code
import React, { Component } from 'react';

export default class App extends Component {

  constructor(props) {
    super(props)

    this.state = {
      count: 0
    }
  }

  handleIncrement = (a)= > {
    this.setState({
      count: this.state.count + 1
    })
  }

  handleDecrement = (a)= > {
    this.setState({
      count: this.state.count - 1
    })
  }

  render() {
    return (
      <div className="container">
        <h1 className="text-center mt-5">{this.state.count}</h1>
        <p className="text-center">
          <button onClick={this.handleIncrement} className="btn btn-primary mr-2">Increase</button>
          <button onClick={this.handleDecrement} className="btn btn-danger my-2">Decrease</button>
        </p>
      </div>); }}Copy the code

This example is pretty simple, but we should think about how React changes this number.

There are two key steps. First, it reads the initial value from state, and then calls the setState method when a click event occurs to change the value of state and re-render the page. Now you can see how the numbers on the page can change.

The problem is that a component in React requires only state and setState to maintain data. What if multiple components need to maintain this data?

2. Why Redux

Anyone who knows how React components transfer data between them should know that React transfers data level by level. As shown on the left below, the green component would have to call back up twice and pass down once to pass data to the red component at some point, which is very troublesome.

Redux has a core component called the Store. The Store manages data independently of the React component. If a data in the React component changes at some point, the state changes. You can directly change the data managed in the Store, so that other components can just grab the data at that time without passing it around.

React has a context that does the same thing, but it’s not recommended, so it won’t be mentioned in this article.

The process seems simple enough, but Redux had to go through a more complicated process to get it right.

Let’s start our Redux tour.

3. How to use Redux

Install the story:

$ npm install --save redux
or
$ yarn add redux
Copy the code

Start by creating a SRC/reducer.js

A Store is usually used in conjunction with a Reducer, which stores data and a Reducer is a pure function that receives and updates data.

First create a Reducer. For simplicity, write the required initial value directly to the state in the Reducer. State = 0 is an initialization data given to the Reducer (state can be an object, here it is directly given a number), and it receives an action. When the action type is ‘increment’, increment state + 1; otherwise, increment state + 1.

Although the initial value is written into the Reducer, it is the Store that really stores this state, and the reducer is responsible for receiving, updating and returning new data.

The Reducer should update the data based on the input action type.

export default (state = 0, action) => {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    default:
      returnstate; }};Copy the code

Then create SRC/store.js

With Reducer we can create the store we need. This store is global and can be imported by any React component that wants to use it.

import { createStore } from 'redux';
import Reducer from './Reducer';

const store = createStore(Reducer)

export default store;
Copy the code

Finally, modify our app.js

import React, { Component } from "react";
+ import store from "./Store";

export default class App extends Component {
+ onIncrement = () => {
+ store.dispatch({
+ type: "increment"
+});
+};

+ onDecrement = () => {
+ store.dispatch({
+ type: "decrement"
+});
+};

  render() {
+ store.subscribe(() => console.log("Store is changed: " + store.getState()));

    return (
      <div className="container">
+ 

{store.getState()}

<p className="text-center"> <button className="btn btn-primary mr-2" onClick={this.onIncrement}> Increase </button> <button className="btn btn-danger my-2" onClick={this.onDecrement}> Decrease </button> </p> </div> ); }}Copy the code

Store.getstate () is used to get the value of state in the store. The store.subscribe() method is used to listen for state in the store, and if state is changed, it will be triggered, so this method receives a function. Subscribe () can also be written inside componentDidMount().

As stated earlier, to change the value of state in a store, you pass in an action’s type value, which redux states must be dispatched by the Store dispatch method.

Store. Dispatch ({type: ‘increment’}); This simple script easily passes the desired value to the reducer, at which point the state value can change.

Now verify the above operation, manually change the value of state, find the page data also changed, indicating that the page data was successfully fetched from the store:

When the click event is triggered, you can see that the value of state has been successfully changed, indicating that the action type dispatched with store.dispatch() is successful.

React uses setState(), which also re-renders the render() function. The setState() method is used to render the render() function.

So you can actually use this setState() method here as well

Modify the App. Js

import React, { Component } from "react";
import store from "./Store";

export default class App extends Component {
+ constructor(props) {
+ super(props);

+ this.state = {
+ count: store.getState()
+};
+}

  onIncrement = () => {
    ...
  };

  onDecrement = () => {
    ...
  };

  render() {
+ store.subscribe(() =>
+ this.setState({
+ count: store.getState()
+})
+);

    return (
      ...
      );
  }
}
Copy the code

The React setState method allows you to re-render the page when the value in the store changes.

A simple Redux example is done here.

3.1. Extract Action

The above example uses the store.dispatch() method to dispatch the Action type in the function triggered by the onClick event. This Action can also be extracted separately

New SRC/Action. Js

export const increment = (a)= > {
  return {
      type: "increment"
  };
};

export const decrement = (a)= > {
  return {
      type: "decrement"
  };
};
Copy the code

Modify the App. Js

import React, { Component } from "react";
import store from "./Store";
+import * as Action from './Action'

export default class App extends Component {
  ...

  onIncrement = () => {
+ store.dispatch(Action.increment());
  };

  onDecrement = () => {
+ store.dispatch(Action.decrement());}; render() { ... }}Copy the code

This extracts the contents of dispatch separately, and the contents of action.js represent the actions of the user’s mouse.

This action.js can be extracted further because the value of type is a constant, so it can be extracted separately

New ActionTypes. Js

export const INCREMENT = 'increment'

export const DECREMENT = 'decrement'
Copy the code

You can then modify actions.js

+import * as ActionTypes from './ActionType';.Copy the code

The reducer.js type can also be modified

+import * as ActionTypes from './ActionType';

export default (state = 0, action) => {
  switch (action.type) {
+ case ActionTypes.INCREMENT:
      return state + 1;
+ case ActionTypes.DECREMENT:return state - 1; default: return state; }};Copy the code

Here is a complete redux example containing Action, Reducer, Store, Dispatch, subscribe, and View. View here refers to the page provided by app.js, which is the React component.

At this point, a quick look at the picture will help you understand the Redux workflow:

How to use react-redux

Using Redux in React could be even more elegant. Redux also provides a react-Redux plugin, but it is important to note that this plugin is a helper and not a replacement for Redux.

As for how to be more elegant with it, let’s start with the code:

Install the react – story:

$ npm install --save react-redux
or
$ yarn add react-redux
Copy the code

Store is used in each component, so it needs to be imported separately. Alternatively, it can be passed directly to the top component and received when the component wants to use it:

Modify index.js, the topmost component, to introduce store here and pass it down

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
+import store from "./Store";
+import { Provider } from 'react-redux'

import registerServiceWorker from "./registerServiceWorker";

ReactDOM.render(
+ 
      
    <App />
+ ,
  document.getElementById("root")
);
registerServiceWorker();
Copy the code

Update app.js to use connect to receive store, and then use this.props to use dispatch, and state to receive store with a function.

import React, { Component } from "react";
import * as Action from "./Action";
+import { connect } from "react-redux";
-import store from "./Store";

+class App extends Component {
- constructor(props) {
- super(props);

- this.state = {
- count: store.getState()
-};
-}

  onIncrement = () => {
+ this.props.dispatch(Action.increment());
  };

  onDecrement = () => {
+ this.props.dispatch(Action.decrement());
  };

  render() {
- store.subscribe(() =>
- this.setState({
- count: store.getState()
-})
-);

    return (
      <div className="container">
+ 

{this.props.count}

<p className="text-center"> <button className="btn btn-primary mr-2" onClick={this.onIncrement}> Increase </button> <button className="btn btn-danger my-2" onClick={this.onDecrement}> Decrease </button> </p> </div> ); }}+const mapStateToProps = state => ({ + count: state +}); +export default connect(mapStateToProps)(App); Copy the code

You may be surprised to find that you don’t need itsetStateMethod to rerender the page, which Redux has done for us. You might be rightconnect()()This is a bit of a puzzle, it’s a Coriolization function, and I won’t discuss the underlying implementation in this article, but I just need to know what parameters are passed in and how to use it.

4.1. Handle Actions

With react-Redux, we don’t need to worry about rerendering the page. We don’t need to manually distribute the Action. We can just pass the Action in the connect method and call it

Modify app.js again based on the above:

import React, { Component } from "react";
import * as Action from "./Action";
import { connect } from "react-redux";

class App extends Component {
- onIncrement = () => {
- this.props.dispatch(Action.increment());
-};

- onDecrement = () => {
- this.props.dispatch(Action.decrement());
-};

  render() {
+ const { increment, decrement } = this.props;

    return (
      <div className="container">
        <h1 className="text-center mt-5">{this.props.count}</h1>
        <p className="text-center">
+ 
            Increase
          </button>
+ 
            Decrease
          </button>
        </p>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  count: state
});

+export default connect(mapStateToProps,Action)(App);
Copy the code

You should be able to get a sense of use from herereact-reduxThe convenience of plug-ins, in fact, it has many other advantages, not one here.

5. How to use multiple reducer

There is only one global store in REdux. If other states need to be managed, multiple reducers may be needed. Redux provides a combineReducers that can connect multiple reducers.

In order to modify the files as little as possible, I did not create folders to classify and manage the files with different functions in the actual use process should be placed in different folders.

Create the file SRC/reducer2.js

export default (state = "hello", action) => {
  switch (action.type) {
    default:
      returnstate; }};Copy the code

Create a SRC/CombineReducer. Js

import { combineReducers } from 'redux';

import count from './Reducer';

import hello from './Reducer2';

const rootReducer = combineReducers({
    count,
    hello
})

export default rootReducer;
Copy the code

Modify Store. Js

import { createStore } from 'redux';
+import rootReducer from './CombineReducer';

+const store = createStore(rootReducer)

export default store;
Copy the code

Modify the App. Js

. return ( <div className="container">+ 

{this.props.text}{this.props.count}

. </div> ); } } const mapStateToProps = state => ({ count: state.count,+ text: state.hello}); .Copy the code

The effect is as follows, and the corresponding value can be read in store:

Finally, the full code is here: github.com/bgrc/react-…