Learn how to React with Redux. Learn how to React with Redux. Learn how to React with Redux. After learning this chapter, you’ll be ready to start your React project.

I have seen the redux construction of a great god in Sifu before and forgot the name of the great god. Here I can only remember the content and write down this article by virtue of my memory and the learning route at that time

Video tutorial

I encountered many difficulties in learning React-Redux, especially I did not understand why it was used, what it was and what it was used for. It’s hard to understand how to use react-Redux with all the nouns, so I’ve decided to start with the principles of react-Redux.

Prior to the start

There are a few things you need to know before this article begins, and OF course I will teach you all about them.

First of all, you need to know the basics of React (which is nonsense). You have some understanding of higher-order functions. There are ES6 basics that satisfy all three of these.

React Context context

The React website says that context is something you’ll probably never use, but if you use React-redux you’re still inadvertently using it. So what is it? You can think of it as a global thing that passes data, because there’s no official definition of context.

So let’s just look at how does it make data globally usable

You need to know these things before you use context

The first thing you need to

import PropTypes from ‘prop-types’;

Prop-types is a thing that does type checking for you so we just use it

And then childContextTypes is this property which is an object that says we’re going to pass the following property name and type through the context which is usually in the parent component

And then getChildContext(){} which is a prescribed method that retrun an object that initializes the data of the context

And then finally, contextTypes property which is also an object that says we’re going to receive the context to pass to the property name and type that’s usually in the child component

Ok, so with that in mind let’s start writing the first context

    // App.js
import React,{Component} from 'react'
import PropTypes from 'prop-types'  / / introduction

export default class App extends Component {
    static childContextTypes = {  // Declare something to pass through the context
        propA: PropTypes.string,
        methodA: PropTypes.func
      }

        getChildContext () {  // Initialize the context
            return {
            propA: 'propA'.methodA: (a)= > 'methodA'}}constructor() {super(a)this.state={
          
        }
    }
    componentWillMount(){
        // console.log(hashHistory)
    }
    render() {
        return (
            <div>
                <Children />
            </div>)}}// Define subcomponent 1 for demonstration purposes

class Children extends Component{
    constructor() {super(a)this.state={
            
        }
    }
    static contextTypes = {   // Specify what to receive
        propA: PropTypes.string
      }
 
    render(){
        console.log(this.context.methodA)  // Undefined because there is no stipulation
        return(
            <div>
                <ChildrenTwo /> 
                <h1>{this.context.propA} </h1>{/* display propA */}</div>)}}ChildrenTwo is a child of Children but gets the value from the App through the context

class ChildrenTwo extends Component{
    static contextTypes={
        methodA: PropTypes.func
    }
    constructor() {super(a)this.state={
        
        }
    }
    render(){
        return(
            <div>
                <h1>{this.context.methodA()}</h1>{/* show methodA */}</div>)}}Copy the code

In layman’s terms a component returns an object with getChildContext and that’s our context declared with childContextTypes and all of its lower components can be declared with contextTypes. We then get the content through this.context and use it.

Okay, so the context is done here, and you put it in the background of your brain and it’s running, and you’re probably in the middle of your head, talking about this dry stuff. Ok, let’s implement a Redux architecture!

From scratch Redux

We create an HTML file called redux.html and we write everything in this one HTML.


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="myHead"></div>
    <div id="myBody"></div>
    <! -- Let's define two basic DOM's here -->
</body>
<script>
    const state={
        myHead: {color:"red".context:"I am the head."
        },
        myBody: {color:"blue".context:"I am the body."}}// Simulate the state

    // Then we declare three render functions
    function renderMyHead(myHead){
        var DOM = document.getElementById('myHead')
        DOM.innerHTML = myHead.context
        DOM.style.color = myHead.color
    }

    function renderMyBody(myBody){
        var DOM = document.getElementById('myBody')
        DOM.innerHTML = myBody.context
        DOM.style.color = myBody.color
    }

    function renderApp(state){
        renderMyHead(state.myHead)
        renderMyBody(state.myBody)
    }
    renderApp(state)
</script>
</html>

Copy the code

The above code renders the contents of the state into the view via function rendering, but the state is exposed and can be modified anywhere. In this way, there will be no stability at all. Let’s imagine that if we stipulate that the state you modify actively will be ignored by the program directly. Only if you modify it through the method I give you, will I accept this state. Hence the dispatch, which is the only place to modify data.


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="myHead"></div>
    <div id="myBody"></div>
    <! -- Let's define two basic DOM's here -->
    <! -- Define a button to trigger dispatch -->
    <button onclick='change()'>Call to dispatch</button>
</body>
<script>
    const state={
        myHead: {color:"red".context:"I am the head."
        },
        myBody: {color:"blue".context:"I am the body."}}// Simulate the state

    // Then we declare three render functions
    function renderMyHead(myHead){
        var DOM = document.getElementById('myHead')
        DOM.innerHTML = myHead.context
        DOM.style.color = myHead.color
    }

    function renderMyBody(myBody){
        var DOM = document.getElementById('myBody')
        DOM.innerHTML = myBody.context
        DOM.style.color = myBody.color
    }

    function renderApp(state){
        renderMyHead(state.myHead)
        renderMyBody(state.myBody)
    }

    // We declare a dispatch function here

    function dispatch(action){
        switch (action.type){
            case 'UPDATE_HEAD_COLOR':
                state.myHead.color=action.color
            break;
            case 'UPDATE_HEAD_CONTEXT':
                state.myHead.context=action.context
            break;
            default:
            break; }}function  change(){
        // Write a method to trigger dispatch
        dispatch({type:'UPDATE_HEAD_COLOR'.color:'black'})
        dispatch({type:'UPDATE_HEAD_CONTEXT'.context:'I've changed.'})
        // Render after update
        renderApp(state)
    }
    renderApp(state)
</script>
</html>

Copy the code

Now you can modify state content through dispatch, and you have to change it regularly according to how it is declared and modified.

It’s time to create a Store

Now that we have the data and can modify it, can we create our repository? It’s called store, and of course, if we were to stuff these things in manually, it would be too low, but it’s extremely comfortable to use functions as a factory to generate this for us.


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="myHead"></div>
    <div id="myBody"></div>
    <! -- Let's define two basic DOM's here -->
    <! -- Define a button to trigger dispatch -->
    <button onclick='change()'>Call to dispatch</button>
</body>
<script>
    const state={
        myHead: {color:"red".context:"I am the head."
        },
        myBody: {color:"blue".context:"I am the body."}}// Simulate the state

    // Then we declare three render functions
    function renderMyHead(myHead){
        var DOM = document.getElementById('myHead')
        DOM.innerHTML = myHead.context
        DOM.style.color = myHead.color
    }

    function renderMyBody(myBody){
        var DOM = document.getElementById('myBody')
        DOM.innerHTML = myBody.context
        DOM.style.color = myBody.color
    }

    function renderApp(state){
        renderMyHead(state.myHead)
        renderMyBody(state.myBody)
    }

    // We declare a dispatch function here

    function stateChanger(state,action){   // After wrapping, we need to tell it where the state comes from
        switch (action.type){
            case 'UPDATE_HEAD_COLOR':
                state.myHead.color=action.color
            break;
            case 'UPDATE_HEAD_CONTEXT':
                state.myHead.context=action.context
            break;
            default:
            break; }}function creatStore(state,stateChanger){  // Here we create a function where the first argument is the state store we want to use and the second argument is our own dispatch
        const getState = (a)= > state
        const dispatch = (action) = > stateChanger(state,action)  // State is the state we put in and action is passed in when we call it
        return{getState,dispatch}
    }

    const store = creatStore(state,stateChanger) // Here we generate store

    renderApp(store.getState())  / / rendering
    function change(){
        store.dispatch({type:'UPDATE_HEAD_COLOR'.color:'black'})  // Change the state value
        store.dispatch({type:'UPDATE_HEAD_CONTEXT'.context:'I've changed.'}) // Change the state value
        renderApp(store.getState()) / / rendering
    }


</script>
</html>

Copy the code

So far we’ve seen a little bit of Redux, but we have to manually call render every time, which is pretty annoying. Next we need to listen for changes in the data and let it render the data itself. So where is this tap? Yeah, in the store.

Setting up Data Listening

Wouldn’t it be nice if we added render data to dispatch? Yeah, but we do have to work at Dispatch.


    function creatStore(state,stateChanger){  // Here we create a function where the first argument is the state store we want to use and the second argument is our own dispatch
        const getState = (a)= > state
        const dispatch = (action) = > {
              stateChanger(state,action) 
            // Here we change the state and then we need to refresh the view
              renderApp(state)
        }  // State is the state we put in and action is passed in when we call it
        return{getState,dispatch}
    }

    const store = creatStore(state,dispatch) // Here we generate store

    renderApp(store.getState())  / / rendering
    store.dispatch({type:'UPDATE_HEAD_COLOR'.color:'black'})  // Change the state value
    store.dispatch({type:'UPDATE_HEAD_CONTEXT'.context:'I've changed.'}) // Change the state value
    // Now we can listen for data changes
Copy the code

However, there is a problem that the creatStore is only applicable to our current project and cannot be used universally. What should I do? It’s actually pretty simple and we just pass in the render method dynamically so we’ll change the code to look like this


    function creatStore(state,stateChanger){  // Here we create a function where the first argument is the state store we want to use and the second argument is our own dispatch
        const getState = (a)= > state
        const listenerList = []
        const subscribe = (listener) = > listenerList.push(listener)
        const dispatch = (action) = > {
              stateChanger(state,action) 
            // Here we change the state and then we need to refresh the view
              listenerList.map(item= >item())
        }  // State is the state we put in and action is passed in when we call it

        return{getState,dispatch,subscribe}
    }

    const store = creatStore(state,stateChanger) // Here we generate store
    store.subscribe((a)= >renderApp(store.getState()))
    renderApp(store.getState())  / / rendering
    store.dispatch({type:'UPDATE_HEAD_COLOR'.color:'black'})  // Change the state value
    store.dispatch({type:'UPDATE_HEAD_CONTEXT'.context:'I've changed.'}) // Change the state value
    // Now we can join listener dynamically
Copy the code

Performance optimization

So here’s the problem again, every time we change a piece of data or we don’t change a piece of data any time we call dispatch we’re going to trigger a full refresh so let’s add console.log and let’s see


    Function renderMyHead(myHead){function renderMyHead(myHead){
    function renderMyHead(myHead){
        console.log("Rendered Head")
        var DOM = document.getElementById('myHead')
        DOM.innerHTML = myHead.context
        DOM.style.color = myHead.color
    }

    function renderMyBody(myBody){
        console.log("Rendered the Body")
        var DOM = document.getElementById('myBody')
        DOM.innerHTML = myBody.context
        DOM.style.color = myBody.color
    }

    function renderApp(state){
        console.log("Rendered App")

        renderMyHead(state.myHead)
        renderMyBody(state.myBody)
    }

Copy the code

So with the console you can see that we’ve only changed the head but the body has also been re-rendered and that’s a huge waste of performance so what do we do? Yeah. Check to see if the data has changed before rendering

But let’s start with a question


    function renderMyHead(newMyHead,oldMyHead={}){
        if(newMyHead==oldMyHead){
            return 
        }
        console.log("Rendered Head")
        var DOM = document.getElementById('myHead')
        DOM.innerHTML = newMyHead.context
        DOM.style.color = newMyHead.color
    }
    function renderMyBody(newMyBody,oldMyBody={}){
        if(newMyBody===oldMyBody){
            return
        }
        console.log("Rendered the Body")
        var DOM = document.getElementById('myBody')
        DOM.innerHTML = newMyBody.context
        DOM.style.color = newMyBody.color
    }

    function renderApp (newState, oldState = {}) {
        if (newState === oldState) {
            return
        }
        renderMyHead(newState.myHead, oldState.myHead)
        renderContent(newState.myBody, oldState.myBody)
    }

    const store = creatStore(state,dispatch) // Here we generate store
    let oldState = store.getState()

    store.subscribe((a)= >{
        const newState = store.getState() // Data may change, get new state
        renderApp(newState,oldState)  // Upload old and new data to the forbidden area
        oldState = newState // Record data
    })
    renderApp(store.getState())  / / rendering
    store.dispatch({type:'UPDATE_HEAD_COLOR'.color:'black'})  // Change the state value
    store.dispatch({type:'UPDATE_HEAD_CONTEXT'.context:'I've changed.'}) // Change the state value

Copy the code

Ok, so that’s where the question comes in, is it useful for us to write this?

The obvious answer is that when we do this, it equals

    let obj = {cc:1}
    let oldObj = obj
    obj.cc = 3 
    obj===oldObj  // true
Copy the code

They all point to the same address. What does that do

So what we need to do now is to change the state return mode inside stateChanger. We no longer return the value, but return the object. When there is an object returned, our newState is definitely not equal to oldState


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="myHead"></div>
    <div id="myBody"></div>
    <! -- Let's define two basic DOM's here -->
</body>
<script>
    const state={
        myHead: {color:"red".context:"I am the head."
        },
        myBody: {color:"blue".context:"I am the body."}}// Simulate the state

    // Then we declare three render functions
    function renderMyHead(newMyHead,oldMyHead={}){
        if(newMyHead===oldMyHead){  // Do not render when the data is the same
            return
        }
        console.log("Rendered Head")
        var DOM = document.getElementById('myHead')
        DOM.innerHTML = newMyHead.context
        DOM.style.color = newMyHead.color
    }

    function renderMyBody(newMyBody,oldMyBody={}){
        if(newMyBody===oldMyBody){  // Do not render when the data is the same
            return    
        }
            console.log("Rendered the Body")
        var DOM = document.getElementById('myBody')
        DOM.innerHTML = newMyBody.context
        DOM.style.color = newMyBody.color
    }

    function renderApp(newState,oldState={}){
        console.log('come',newState,oldState)
        if(newState===oldState){  // Do not render when the data is the same
            return
        }
        console.log("Rendered App")
        renderMyHead(newState.myHead,oldState.myHead)
        renderMyBody(newState.myBody,oldState.myBody)
    }

    // We declare a dispatch function here

    function stateChanger(state,action){   
        switch (action.type){
            case 'UPDATE_HEAD_COLOR':
                return{   // Here we use ES6 to return a new state instead of changing the original state. We also need to change the dispatch method in creatStore. state,myHead: {... state.myHead,color:action.color
                    }
                }
            break;
            case 'UPDATE_HEAD_CONTEXT':
               return{
                    ...state,
                    myHead: {... state.myHead,context:action.context
                    }
               }
            break;
            default:
                return{... state}break; }}function creatStore(state,stateChanger){  // Here we create a function where the first argument is the state store we want to use and the second argument is our own dispatch
        const getState = (a)= > state
        const listenerList = []
        const subscribe = (listener) = > listenerList.push(listener)
        const dispatch = (action) = > {
             state = stateChanger(state,action)   // Here we just override the original state
            // Here we change the state and then we need to refresh the view
              listenerList.map(item= >item())
        }  
        return{getState,dispatch,subscribe}
    }

    const store = creatStore(state,stateChanger) // Here we generate store
    let oldStore = store.getState()   // Cache old data
    store.subscribe((a)= >{
        let newState = store.getState()  // Get new data
        renderApp(newState,oldStore)   // Call compare render
        oldStore = newState   // Data cache
    }) 
    renderApp(store.getState())
    store.dispatch({type:'UPDATE_HEAD_COLOR'.color:'black'})  // Change the state value
    store.dispatch({type:'UPDATE_HEAD_CONTEXT'.context:'I've changed.'}) // Change the state value
    // After some refinement we no longer call the Body render
</script>
</html>

Copy the code

Now that we’ve built a simple Redux of our own, let’s move on to React-Redux

reducer

When we say creatStore, we pass in two parameters: state and stateChanger. Can we merge these two parameters together? No problem. The reducer of our React-redux was after the merger

    // We'll start with stateChanger
    function stateChanger(state,action){
        If there is no state we return one
        if(! state){return{
                myHead: {color:"red".context:"I am the head."
                },
                myBody: {color:"blue".context:"I am the body."}}}switch (action.type){
            case 'UPDATE_HEAD_COLOR':
                return{   // Here we use ES6 to return a new state instead of changing the original state. We also need to change the dispatch method in creatStore. state,myHead: {... state.myHead,color:action.color
                    }
                }
            break;
            case 'UPDATE_HEAD_CONTEXT':
               return{
                    ...state,
                    myHead: {... state.myHead,context:action.context
                    }
               }
            break;
            default:
                return{... state}break; }}function creatStore(stateChanger){  // Now we don't need to pass state, we just need to pass stateChanger because we can get it
        let state = null
        const getState = (a)= > state
        const listenerList = []
        const subscribe = (listener) = > listenerList.push(listener)
        const dispatch = (action) = > {
             state = stateChanger(state,action)   // Here we just override the original state
            // Here we change the state and then we need to refresh the view
              listenerList.map(item= >item())
        }
        dispatch({}) // Initialize state here
        State = stateChanger(state,action); // We just call dispatch({}) once. This gives us the state we set inside stateChanger
        return{getState,dispatch,subscribe}
    }

        const store = creatStore(stateChanger) // Here we generate store and don't pass in state just put our stateChanger in it
        // This stateChanger is officially called reducer
            let oldStore = store.getState()   // Cache old data
            store.subscribe((a)= >{
                let newState = store.getState()  // Get new data
                renderApp(newState,oldStore)   // Call compare render
                oldStore = newState   // Data cache
            }) 
            renderApp(store.getState())
            store.dispatch({type:'UPDATE_HEAD_COLOR'.color:'black'})  // Change the state value
            store.dispatch({type:'UPDATE_HEAD_CONTEXT'.context:'I've changed.'}) // Change the state value
            // After some refinement we no longer call the Body render

Copy the code

At this point, you’ll suddenly find yourself implementing a redux! We still have a process to go through to integrate react.

conclusion

In the first part of our fourth chapter, we built our own redux from scratch, which involves too many advanced things, we need to digest, don’t understand the message must ask questions ~~

Video in production