Redux introduces

Redux is a JavaScript application for managing data state and UI state. With the increasing complexity of JavaScript single page application (SPA) development, JavaScript needs to manage more states than ever before, and Redux makes it easier to manage. (Redux supports React, Angular, jQuery, and even pure JavaScript)

Create store repository

NPM Install is required to install Redux before using it

npm install redux --save 
Copy the code

After redux is installed, create a store folder in the SRC directory, and then create an index.js file under that folder.

Index.js is the store file for the entire project. Open the file and write the following code.

import { createStore } from 'redux'Const store = createStore() // Create datastore repositoryexportDefault store // exposedCopy the code

In this way, although the warehouse has been established, but the warehouse is very chaotic, at this time, it needs a module with management ability, which is Reducers. These two must be created together so that the repository does not play off against each other. In the Store folder, create a new file reducer.js and write the following code.

Const defaultState = {} // DefaultexportDefault (state = defaultState,action)=>{// is a method functionreturn state
}
Copy the code

State: is the data information that needs to be managed in the whole project. There is no data here, so it is represented by empty objects.

Reducer is then established. Introduce reducer into the store and pass the reducer to the store in the form of parameters when creating the store.

import { createStore } from 'redux'Import reducer from createStore'./reducer'// Introduce the Reducers module const Store = createStore(reducer) // Create a data storage repositoryexportDefault store // exposedCopy the code

Components that use data from REdux

  • Initialize the data of defaultState in reducer.js

Add two attributes myName and list to the reducer.js defaultState object. The following code

const defaultState = {
    myName : 'Just tomato and eggs.',
    list:['Scrambled eggs with tomatoes'.'Fried rice with eggs'.'Tomato and Egg soup']}export default (state = defaultState,action)=>{
    return state
}
Copy the code

It’s like you’re adding two new pieces of data to the Store.

  • The component gets the data from the store and now that we have the store repository, we have the data, we can just add a store to the component
// Import store from'./store/index'// Introduce the import store from abbreviation'./store'
Copy the code

Once you’ve introduced the Store, try printing it to the console in the constructor to see if you actually get the data, and if everything works, you can get the data.

Console.log (console.log(console.log(console.log(console.getState ()))}}Copy the code

At this point the data cannot be used directly in the component and needs to be copied to the component’s state in the constructor

Constructor (props){super(props) // To get the state data in store, use the store.getState() method this.state = store.getState(). console.log(this.state)Copy the code

Redux Dev Tools installation

Redux DevTools is similar to Vue DevTools in that it allows you to view data in the store

// Go to the Google Chrome store and search for Redux DevTools installationCopy the code

Configure Redux Dev Tools

import { createStore } from 'redux'Import reducer from createStore'./reducer'Const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() )exportDefault store // exposedCopy the code

After the above configuration, open the debugging tool in the browser to view the data in the Store

Component to change data in redux

Create an Action

To change the value of State in Redux, create an Action. An Action is an object with two properties. The first is a description of the Action and the second is the value to be changed.

const action ={
    type:'change_value'// Action description value:'Value passed to state'// Want to change/save the value}Copy the code

The action is created, but passed to the Store via the dispatch() method. Let’s add another line of code under action.

const action ={
    type:'change_value'// Action description value:'Value passed to state'// Want to change/save the value} store.dispatch(action) // push via store.dispatchCopy the code

At this point, the Action is completely created and associated with the Store.

Store’s automatic push strategy

The Store is just a repository and has no management ability. It automatically forwards the received actions to the Reducer. Print the results directly from the Reducer and see. Open the reducer.js file under the Store folder and modify the code.

export default (state = defaultState,action) => {
    console.log(state, action)
    return state
}
Copy the code
  • State: Refers to the state in the original warehouse.
  • Action: Refers to the newly passed state of the action. It can be seen from the print that Reducer has received the original data and the new data, and now the Reducer needs to change the value in the store. First check whether type is correct. If correct, we need to declare a new variable newState to deeply copy state. (Remember that the Reducer only accepts states, not changes them.) So we declare a new variable and return it again.
export default (state = defaultState,action) => {
    if(action.type === 'change_value') {letParse (json.stringify (state)) // Deep copy state newstate.myName = action.valuereturn newState
    }
    return state
}
Copy the code

Let the component update

Now that the data in the Store has been updated, but the component has not been updated, you need to add the subscribe Redux status function to the component’s constructor

constructor(props){ super(props) this.state=store.getState(); This.storechange = this.storechange. Bind (this) // Change this to store.subscribe(this.storechange) // Subscribe Redux status}storeChange(){this.setState(store.getState()) // Retrieve the latest state}Copy the code

The above business scenario that requires the addition of a subscription redux state function is one that both uses state in the current component and changes state due to its own changes, for example:

render() { 
        return ( 
            <div style={{margin:'10px'}}>
               <input 
                    value={this.state.myName}
                    @onClick={this.changeMyName.bind(this)}> 
               </input>
            </div>
         );
}
changeMyName (e) {
    const action ={
        type:'change_value', // Action description value: e.target.value // The value you want to change/save} store.dispatch(action) // Push via store.dispatch}Copy the code

Redux’s tip

When writing Redux Action, many Action Types are generated due to the distribution of many Action Types. If we need to name the Action by ourselves, two basic problems will occur:

  • If these Types are not managed uniformly, it is not conducive to the use of large projects, and the Settings will generate redundant codes.
  • Because the Type in the Action must correspond to the Type in the Reducer one by one, there is no clear error in the browser after this part of code or letter is written wrong, which brings great difficulty to debugging.

modular

Split the Action Type into a separate file. In the SRC /store folder, create a new actiontypes.js file and manage the Type set in the file.

export const  CHANGE_VALUE = 'change_value'
export const  ADD_ITEM = 'addItem'
export const  DELETE_ITEM = 'deleteItem'
Copy the code

Component to import actiontypes.js and use it

On demand in components that need to be used

import { CHANGE_VALUE } from './store/actionTypes'
Copy the code
ChangeInputValue (e){const action ={type:'change_value', // action description value: e.target.value // want to change/save the value} store. Dispatch (action) const action ={type:CHANGE_VALUE, // Action description value:e.target.value // Value to be changed/saved} store.dispatch(action)}Copy the code

Introduced and used in reducer.js

The actiontype. js file is also introduced, and the corresponding string is changed to a constant, the whole code is as follows:

import { CHANGE_VALUE } from './actionTypes'

const defaultState = {
    myName : 'Just tomato and eggs.',
    list:['Scrambled eggs with tomatoes'.'Fried rice with eggs'.'Tomato and Egg soup']}export default (state = defaultState,action)=>{
    if(action.type === CHANGE_VALUE){
        letParse (json.stringify (state)) // Deep copy state newstate.myName = action.valuereturnNewState} // Multiple judgments can be written to change the assignmentreturn state
}
Copy the code

The action in the component is also removed

  • Write the actionactioncreine.js file

In the/SRC /store folder, create a new actionactiontypes.js file and start with the actionTypes.

import {CHANGE_VALUE}  from './actionTypes'// The last execution of this exported anonymous function returns an objectexport const changeInputAction = (value)=>({
    type:CHANGE_VALUE,
    value
})
Copy the code
  • Modify the way used in the component
import {changeInputAction} from './store/actionCreatores'// Introduce the extracted actionCreators. Js changeInputValue(e){const action ={type:'change_value', // action description value: e.target.value // want to change/save the value} store. Dispatch (action) const action ={type:CHANGE_VALUE, Const action = changeInputAction(e.target.value) const action = changeInputAction(e.target.value) store.dispatch(action) }Copy the code

This completes the separation, so you might ask, what’s the point of adding another file and making the code look even more obscure? In fact, this enables reuse, for example, add, delete, change and check can not be used once, may be used in many places, so we can directly introduce the file to use, can avoid redundant code. Also, if a constant name is incorrectly written, the application will report an error directly to the browser and console, which will speed up development and reduce the time to find errors.

Very important points in Redux

  • Stores must be unique. Multiple stores are never allowed. Only one store is allowed
  • Only store can change its content, but Reducer cannot
  • Reducer must be a pure function
Reudcer only returned the changed data, but did not change the data in the store. The store got the data from the Reducer and updated itself.Copy the code
Reducer must be a pure function and always returns the same result if the function has the same call parameters. It does not depend on any changes in state or data outside the function during program execution and must depend only on its input parameters.Copy the code

Axios gets the data asynchronously and combines it with Redux

// add a constant field to actiontypes.jsexport const GAT_DATA = 'getData'
Copy the code
// actionactionine.js adds a method to save the file import {GAT_DATA} from'./actionTypes'// Introduce constantsexport const getDataAction = (data) => ({
    tyle: 'GAT_DATA',
    value: data
})
Copy the code
Import {getDataAction} from is used in the component'./actionCreators'// Import the actionCreators import axios from the module'axios'

componentDidMount(){// call axios.get() in the lifecycle'address').then(res => {
        if (res.data) {
            const data = res.data
            const action = getDataAction(data)
            store.dispatch(action)
        }
    }).catch(err => {
        alert(err)
    })
}
Copy the code

Now that the data has been sent to the store via Dispatch, the reducer needs to handle the business logic. The code to open reducer.js is as follows

import { GAT_DATA, CHANGE_VALUE } from './actionTypes'

const defaultState = {
    myName : 'Just tomato and eggs.',
    list:['Scrambled eggs with tomatoes'.'Fried rice with eggs'.'Tomato and Egg soup']}export default (state = defaultState,action)=>{
    if(action.type === CHANGE_VALUE){
        letParse (json.stringify (state)) // Deep copy state newstate.myName = action.valuereturn newState
    }
    if(action.type === GAT_DATA){
        letParse (json.stringify (state)) // Deep copy state newstate. list = action.value // Save the list of data obtained through AXIosreturnNewState} // Multiple judgments can be written to change the assignmentreturn state
}
Copy the code

Redux-thunk middleware installation and configuration

Redux-thunk Is the most commonly used plug-in for Redux. When will this plugin be used? For example, after dispatching an Action and before arriving at the reducer, additional operations need to be done using middleware. In practice you can use middleware to log, create crash reports, invoke asynchronous interfaces, or route. This middleware can be enhanced with Redux-Thunk

Install the Redux-Thunk component

npm install --save redux-thunk
Copy the code

Configure the redux-thunk component

Import applyMiddleware import {createStore, applyMiddleware} from in redux if you are using middleware'redux'// import thunk from redux-thunk'redux-thunk'
Copy the code

If you follow the documentation, you can simply put thunk as the second parameter in createStore, but we configured Redux Dev Tools to use the second parameter.

Const store = createStore(Reducer, applyMiddleware(thunk)) // Create a data storeCopy the code

This is perfectly fine, but the Redux Dev Tools plugin will not work, and if you want to use both, you need to use the enhancement function. You need to introduce compose before using the increment function.

import { createStore , applyMiddleware ,compose } from 'redux' 
Copy the code

Compose then creates an enhancement function for compose, which is equivalent to creating a chain function as follows:

const composeEnhancers =   window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
Copy the code

With the enhancement function, you can add thunk so that both functions will execute.

const enhancer = composeEnhancers(applyMiddleware(thunk))
Copy the code

This is done by using the enhancer variable as the second parameter in the createStore function.

Const store = createStore(Reducer, enhancer) // Create a data storeCopy the code

The detailed code is as follows:

import { createStore , applyMiddleware ,compose } from 'redux'Import reducer from createStore'./reducer'    
import thunk from 'redux-thunk'const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose const enhancer = composeEnhancers(applyMiddleware(thunk)) const Store = createStore(Reducer, enhancer) // Create a data storage repositoryexportDefault store // exposedCopy the code

How to use redux-thunk

Putting applications that request data from the background into the middleware creates a complete Redux process, all the logic is done in-house in Redux, which looks much better and makes it much easier to automate testing

Write the business logic in actionActionine.js

Actionactionine.js used to be defined actions, so you couldn’t write the business logic. With Redux-Thunk, you can write the componentDidMount business logic in Todolist.js. That is, the code that requests data from the background is put in the actionActioncreans.js file. So we need to introduce Axios and write a new function method. (Once actions were objects, now actions can be functions. That’s what redux-thunk brings.)

import axios from 'axios'

export const getTodoList = () =>{
    return ()=>{
        axios.get('address').then((res)=>{
            const action = getDataAction(data)
            dispatch(action)
        })
    }
}
Copy the code

Use in components

import { getTodoList } from './store/actionCreatores'
componentDidMount(){
    const action = getTodoList()
    store.dispatch(action)
}
Copy the code

At this point we can see that the asynchronously retrieved data has been stored in state using Redux Dev Tools

You may find this cumbersome, and it did at first, but as the project gets bigger, you’ll find that incorporating the business logic of shared state into your Redux prompts makes your program more organized


The above is my understanding and notes in learning Redux, the learning video is through the technical fat B station video and nuggets article, the link is below

Links: juejin. Cn/post / 684490…