Why use Redux

React is a simple view-layer framework. The application developed by React is made up of many components. If the application developed by React is the component structure shown in the following figure, when we want to pass the data of the blue node to the top layer, we need to call the function of the parent component of the current node to indirectly pass the value (React requires basic knowledge to understand react), the parent component then calls the function of the parent component and passes the value, and so on, finally passing the value to the component of the root node. This will make the component value transfer very troublesome, how to solve this problem? In this case, we need to introduce the data layer framework Redux to help us solve the problem of transferring values between complex components. Thus, we can prove that React is only used to help us render DOM. If we want to develop complex projects, just using the React framework is not enough.How does the Redux framework help us with data transfer between components? The idea is that Redux writes all the components’ data to the Store public space, and when we want to pass the blue component data to each component, we put the blue component data into the Store public space, and then the other components listen for the data in the Store space, and once they find that the data has changed, It fetches the data back to its own component, thus achieving data transfer between components.

Redux workflow

As you can see in the figure below, the workflow looks complicated at first glance, but it’s actually quite simple. I’ll use the library borrowing process to illustrate the workflow of Redux.Blue box we can borrow books as a people, orange box as a librarian, purple box as a library book, yellow box as we said to the librarian (for example, I want to borrow a book XXX) and its working process is the borrower (blue) xx said I want to borrow a book, it said that this sentence is yellow box (Action), The administrator hears what book to solve (orange), but he doesn’t know where the book is, so he checks the record book (purple), which informs the administrator (orange), and finally the administrator finds the book and takes it to the borrower (blue). Let me use the React data flow to describe the Redux workflow. First, when the component wants to change data, it informs Store of the data to be changed through Action (yellow), but Store itself does not know the location of the data to be changed, so it needs to use Reducers. Reducers tells Store to change the data, and Store updates the data, and finally passes the updated data to the component.

The Store to create

First we need to create a new project. Once the project is successfully created, we go to the SRC directory and create a todolist.js component as a demo file, with the following code:

import React,{Component} from "react";
class TodoList extends Component{
    constructor(props){
        super(props);
        this.state={
            list: ["The Legend of Zelda - Breath of the Wild."."The Mario Odyssey"."Pokemon Sword and Shield"."Alien Blade 2"]}}render(){
        return (
            <div>
                <input value={this.state.inputVal } placeholder="to do item"/>
                <button>submit</button>
                {
                    this.state.list.map((item,inext)=>{
                        return <div>{item}</div>})}</div>)}}export default TodoList;
Copy the code

The index.js file code is as follows:

import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList.js';

ReactDOM.render(
    <TodoList />.document.getElementById('root'));Copy the code

The page renders as follows:If we want to use Redux to manage the data, we need to install Redux, start the command line tool, and enter the project:

cnpm install redux --save
Copy the code

After the installation is complete, we create a folder named Store in the SRC directory. Using the library example again, we know that the librarian (Store) does not know what books are in the library and where they are. He has to use the log book to see the information about the books. Therefore, we first create this record, which is the reducer. Js file under the store file, with the following code:

let defaultState={
    inputVal:"".list: ["The Legend of Zelda - Breath of the Wild."."The Mario Odyssey"."Pokemon Sword and Shield"."Alien Blade."]};export default (state=defaultState,action)=>{
    return state;
}
Copy the code

DefaultState is the original information about all books in the library. State is the information about books returned from the log book to the librarian. Now that we have the log book, we can create a store (librarian) and create index.js under the Store folder as follows:

import {createStore} from 'redux'
import reducer from './reducer'
const store=createStore(reducer);
export default store;
Copy the code

CreateStore was introduced to create a store, but the librarian does not know the information about the books, so we need to reducer the record to this store. Now that the administrator has it, and you have the log book, you can borrow books from the administrator (for data). We modified the previous todolist.js code as follows:

import React,{Component} from "react";
import store from './store/index.js'
class TodoList extends Component{
    constructor(props){
        super(props);
        this.state=store.getState();
    }
    render(){
        return (
            <div>
                <input value={this.state.inputVal } placeholder="to do item"/>
                <button>submit</button>
                {
                    this.state.list.map((item,inext)=>{
                        return <div>{item}</div>})}</div>)}}export default TodoList;
Copy the code

The code above introduces the Store, which gets book information (data) via the getState() method and renders it on the page. Summary: Create a store using createStore in REdux, then create a reducer function and pass this function as a parameter to createStore, and finally get the data rendered in the page using store.getState().

Compile Action and Reducer

Install the react-devTools debugging tool to check the data in the future. Download the tool and drag it to the Extension window of Google Chrome to use it. Here you need to configure the store index.

import {createStore} from 'redux'
import reducer from './reducer'
const store=createStore(reducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
Copy the code

If a component wants to change data in a Store, it first creates an Action that tells the store what data it wants to change. The Store does not know how to modify the data. It will forward the data and actions in the original store to the Reducer. The Reducer will get the data in the original store and know which data to change and the value of the changed data. Reducer returns the new data to the store (note: Reducer can obtain the data from the store, but it cannot directly modify the data. It can only modify the copied data and finally return it to the Store). Then the store replaces the original data with new data. So the Store gives us a subscribe method, which is used to subscribe to the data in the Store, and if the data changes, it executes the argument passed to the SUBSCRIBE method (which is a function), which we use to re-render the data in the component. The following will improve the TodoList function, and we will make the following modifications to reducer.

let defaultState={
    inputVal:"".list: ["The Legend of Zelda - Breath of the Wild."."The Mario Odyssey"."Pokemon Sword and Shield"."Alien Blade 2"]};export default (state=defaultState,action)=>{
    if(action.type==="input_change_value") {let newState=JSON.parse(JSON.stringify(state));
        newState.inputVal=action.value;
        return newState;
    }else if(action.type==="input_submit_value") {let newState=JSON.parse(JSON.stringify(state));
        newState.inputVal="";
        newState.list.push(action.value);
        return newState;        
    }else if(action.type==="input_delete_value") {let newState=JSON.parse(JSON.stringify(state));
        newState.list.splice(action.value,1);
        return newState;        
    }
    return state;
}
Copy the code

Modify todolist.js as follows:

import React,{Component} from "react";
import store from './store/index.js'
class TodoList extends Component{
    constructor(props){
        super(props);
        this.state=store.getState();
        this.handleSubmit=this.handleSubmit.bind(this);
        this.changeInput=this.changeInput.bind(this);
        this.changeStore=this.changeStore.bind(this);      
        store.subscribe(this.changeStore);
    }
    render(){
        return (
            <div>
                <input value={this.state.inputVal } onChange={this.changeInput} placeholder="to do item"/>
                <button onClick={this.handleSubmit}>submit</button>
                {
                    this.state.list.map((item,index)=>{
                        return <div key={index} onClick={this.delItem.bind(this,index)}>{item}</div>})}</div>)}changeInput(e){
        let action={
            type:"input_change_value".value:e.target.value
        }
        store.dispatch(action);
    }
    handleSubmit(){
        let value=this.state.inputVal;
        let action={
            type:"input_submit_value",
            value
        }
        store.dispatch(action);        
    }
    delItem(index){
        let value=index;
        let action={
            type:"input_delete_value",
            value
        }
        store.dispatch(action);     
    }
    changeStore(){
        this.setState(store.getState()); }}export default TodoList;
` `'javascript may be more troublesome to write for the first time, but as long as you write one, it is basically copy and paste, the general idea is to write code according to the flow chart. The aticon attribute in todolist.js has a type attribute that corresponds to the action type attribute in the reducer.js root. If we accidentally misspell type, the console will not report any errors. When this happens, it can be very difficult to debug, and you can spend an hour or two looking for a bug that turns out to be a spelling error, which can be really frustrating, so split actiont Type to optimize. We create actiontypes.js in the Store folder as follows: '` `javascript
export const INPUT_CHANGE_VALUE="input_change_value";
export const INPUT_SUBMIT_VALUE="input_submit_value";
export const INPUT_DELETE_VALUE="input_delete_value";
Copy the code

Reducer. Js code is modified as follows:

import {INPUT_CHANGE_VALUE,INPUT_SUBMIT_VALUE,INPUT_DELETE_VALUE} from "./actionTypes.js"
let defaultState={
    inputVal:"".list: ["The Legend of Zelda - Breath of the Wild."."The Mario Odyssey"."Pokemon Sword and Shield"."Alien Blade 2"]};export default (state=defaultState,action)=>{
    if(action.type===INPUT_CHANGE_VALUE){
        let newState=JSON.parse(JSON.stringify(state));
        newState.inputVal=action.value;
        return newState;
    }else if(action.type===INPUT_SUBMIT_VALUE){
        let newState=JSON.parse(JSON.stringify(state));
        newState.inputVal="";
        newState.list.push(action.value);
        return newState;        
    }else if(action.type===INPUT_DELETE_VALUE){
        let newState=JSON.parse(JSON.stringify(state));
        newState.list.splice(action.value,1);
        return newState;        
    }
    return state;
}
Copy the code

Todelist.js code is modified as follows:

import React,{Component} from "react";
import store from './store/index.js'
import {INPUT_CHANGE_VALUE,INPUT_SUBMIT_VALUE,INPUT_DELETE_VALUE} from "./store/actionTypes.js"
class TodoList extends Component{
    constructor(props){
        super(props);
        this.state=store.getState();
        this.handleSubmit=this.handleSubmit.bind(this);
        this.changeInput=this.changeInput.bind(this);
        this.changeStore=this.changeStore.bind(this);      
        store.subscribe(this.changeStore);
    }
    render(){
        return (
            <div>
                <input value={this.state.inputVal } onChange={this.changeInput} placeholder="to do item"/>
                <button onClick={this.handleSubmit}>submit</button>
                {
                    this.state.list.map((item,index)=>{
                        return <div key={index} onClick={this.delItem.bind(this,index)}>{item}</div>})}</div>)}changeInput(e){
        let action={
            type:INPUT_CHANGE_VALUE,
            value:e.target.value
        }
        store.dispatch(action);
    }
    handleSubmit(){
        let value=this.state.inputVal;
        let action={
            type:INPUT_SUBMIT_VALUE,
            value
        }
        store.dispatch(action);        
    }
    delItem(index){
        let value=index;
        let action={
            type:INPUT_DELETE_VALUE,
            value
        }
        store.dispatch(action);     
    }
    changeStore(){
        this.setState(store.getState()); }}export default TodoList;
Copy the code

This way, if type is misspelled, the console will report an error.

Use actionCreator to create actions collectively

In the previous code, we put the creation of the action in the component. For some simple projects, this can be done, but for large projects, you need to put the creation of the action in a file for unified management, so that the code is relatively high maintainability. We created actionactionine.js in the Store folder with the following code:

import {INPUT_CHANGE_VALUE,INPUT_SUBMIT_VALUE,INPUT_DELETE_VALUE} from "./actionTypes.js"
export let getChangeValueAction=(value) = >{
    return {
        type:INPUT_CHANGE_VALUE,
        value,
    }
}
export let getSubmitValueAction=(value) = >{
    return {
        type:INPUT_SUBMIT_VALUE,
        value,
    }
}
export let getDeleteValueAction=(value) = >{
    return {
        type:INPUT_DELETE_VALUE,
        value,
    }
}
Copy the code

Todolist.js code is modified as follows:

import React,{Component} from "react";
import store from './store/index.js'
import {getChangeValueAction,getSubmitValueAction,getDeleteValueAction} from "./store/actionCreators.js"
class TodoList extends Component{
    constructor(props){
        super(props);
        this.state=store.getState();
        this.handleSubmit=this.handleSubmit.bind(this);
        this.changeInput=this.changeInput.bind(this);
        this.changeStore=this.changeStore.bind(this);      
        store.subscribe(this.changeStore);
    }
    render(){
        return (
            <div>
                <input value={this.state.inputVal } onChange={this.changeInput} placeholder="to do item"/>
                <button onClick={this.handleSubmit}>submit</button>
                {
                    this.state.list.map((item,index)=>{
                        return <div key={index} onClick={this.delItem.bind(this,index)}>{item}</div>})}</div>)}changeInput(e){
        let action=getChangeValueAction(e.target.value);
        store.dispatch(action);
    }
    handleSubmit(){
        let action=getSubmitValueAction(this.state.inputVal);
        store.dispatch(action);        
    }
    delItem(index){
        let action=getDeleteValueAction(index)
        store.dispatch(action);     
    }
    changeStore(){
        this.setState(store.getState()); }}export default TodoList;
Copy the code

The three elements of Redux

1, Store unique, the entire application can only have one store storage space. Second, only the Store can change its own data. Reducer only delivers new data to the Store, and then the Store updates the data. Reducer must be a pure function. The pure function means that given a fixed input, there will be a fixed output and there will be no side effects. For example, some asynchronous operations or time-related operations are not allowed. This is the basic introduction of Redux, and I will write an advanced article if I have time, which will mainly introduce the use of Redux middleware.

conclusion

Life does not disappoint those who keep moving forward.