Redux core concepts

The official Redux documentation defines it as a predictable JavaScript application state management container. It proposed “the only source of truth for data”, one-way data flow and “pure function Reducers”, which greatly simplified the front-end logic and enabled us to manage shared data efficiently and controllable

  • Action: a generic JavaScript object that describes what is going to be done, and is the only thing that triggers changes to the data, so that the changes are manageable and traceable

  • Reducer: A reducer that processes data according to different actions and the data will be saved by the store

  • Store: Data warehouse for storing shared data

The general process of using Redux to update shared data is as follows:

Here’s an example:

import {createStore} from 'redux'
const INCREASE = 'INCREASE' / / add
const DECREASE = 'DECREASE' / /
* @param {any} state Specifies the previous data stored in the repository. * @param {Object} Action Specifies the data operation. */
function reducer(state, action) {
    switch(action.type) {
        case INCREAASE:
            return state + 1
        case DECREASE:
            return state - 1
        default:
            return state
    }
}
CreateStore (Reducer, initialState) Creates a repository to store data
// Start with two parameters: the function to update the data and the initial state value
const store = createStore(reducer, 10) // The initialization state value is 10
const action = { type: INCREASE } // +1 action object

console.log(store.getState()) // Print: 10

// Call the store.dispatch dispatch operation to change the data
store.dispatch(action) / / + 1
store.dispatch(action) / / + 1

console.log(store.getState()) // Print: 12
Copy the code

Action

  • Action must be a plain object, such as an object created directly from a literal (its __proto__ points directly to Object.prototype), and not an instance object created like any inherited form, such as:

    class Action {
        constructor(type){
            this.type = type
        }
    }
    const action = new Action('INCREASE')
    // Calling the dispatch method directly returns an error
    store.dispatch(action)
    Copy the code
  • The Action object must have a type attribute that describes the type of the Action, but does not constrain its type (string, number, symbol, etc.)

  • Action creation function: Generally, standard action objects are created by functions, and easy to manage uniformly

    // action-types.js uses Symbol type
    export const INCREASE = Symbol('increase')
    export const DECREASE = Symbol('decrease')
    export const SET = Symbol('set')
    
    // number-action.js
    import * as actionTypes from './action-types'
    export function getIncreaseAction() {
        return {
            type: actionTypes.INCREASE
        }
    }
    export function getDecreaseAction() {
        return {
            type: actionTypes.DECREASE
        }
    }
    export function getSetAction(value) {
        return {
            type: actionTypes.SET,
            /* The payload field is used to indicate additional data */
            payload: value
        }
    }
    Copy the code
  • To facilitate the dissemination of actions using action creator functions, Redux provides a function called bindActionCreators that enhances the creators of action functions so that it can not only create actions, After being created, the action distribution function can be completed automatically

    import {bindActionCreators} from 'redux'
    import {getIncreaseAction, getDecreaseAction, getSetAction} from 'number-action'
    
    // The first argument is either the object that the action creator merged with or an action creator
    // The second argument is the warehouse's dispatch function
    // The result is determined by the first argument passed in
    // Returns a new function that automatically dispatches the action
    // Returns a new object, where each attribute name is the same as each attribute name of the first parameter object
    
    const actionCreators = {
        increaseNum: getIncreaseAction,
        decreaseNum: getDecreaseAction,
        setNum: getSetAction
    }
    // store The store object created for createStore
    const boundActions = bindActionCreators(actionCreators, store.dispatch)
    
    // When the action is distributed later
    / / direct call boundActions. GetIncreaseAction () to distribute the action
    / / and don't have to write a store. The dispatch (actionTypes. GetIncreaseAction ())
    Copy the code

Reducer

It’s a function that changes the data

A data warehouse has and only one Reducer, and typically a project has only one repository, so a system has only one Reducer

  1. When the Reducer function was called:

    • When an action is distributed through the store.dispatch method

    • When the repository is created, reducer is called (so you can use the reducer function to initialize the state), and the action type is a special type: @@redux/INITxxxxxxx

      // If the default value is not passed when creating the repository, undefined is passed
      // The default values of the reducer function parameters can therefore be taken
      const store = createStore(reducer)
      const initialState = xxx // The initialization state value of the repository
      function reducer(state = initialState, action) {
          switch(action.type){
              // ...
              default:
                  return state
          }
      }
      Copy the code
  2. The reducer function normally uses switch(…) internally. Case statement to determine the type

  3. The Reducer function needs to be a pure function with no side effects that will be useful for testing, debugging, restoring data, and combining with React

  4. When the project is large and the data structure stored is complex, the reducer needs to be subdivided (finally merged into a reducer)

    For example, the shared data has the login user information and the list of all users
    // reducers/loginUserInfo.js
    import {SET_LOGIN_USER_INFO} from './loginUser-types'
    const initialState = null // Initializes the state value
    
    export default (state = initialState, { type, payload }) => {
        switch (type) {
            case SET_LOGIN_USER_INFO:
                return payload
            default:
                return state
        }
    }
    
    // reducers/users.js
    import {SET_USERS} from './users-types'
    const initialState = [] // Initializes the state value
    
    export default (state = initialState, { type, payload }) => {
        switch (type) {
            case SET_USERS:
                return payload
            default:
                return state
        }
    }
    
    // reducers/index.js Merge reducer and pass it to store
    import loginUserReducer from './loginUser'
    import usersReducer from './users'
    
    export default (state = {}, action) => {
        const newState = {
            loginUser: loginUserReducer(state.loginUser, action),
            users: usersReducer(state.users, action)
        }
        return newState
    }
    Copy the code

    As shown in the above code, the state management can be reduced by manually writing and merging the reducer function, and then the reducer can handle the state management. Of course, Redux helped us encapsulate a method combineReducers used to merge our subdivided reducer, which performed the same as the above hand-written merge function

    // reducers/index.js Merge reducer and pass it to store
    import { combineReducers } from 'redux'
    import loginUserReducer from './loginUser'
    import usersReducer from './users'
    
    export default combineReducers({
        loginUser: loginUserReducer,
        users: usersReducer
    })
    
    // store.getState() gets the property name of state, as determined by the property name of the merge processing object
    Copy the code

Store

CreateStore is a repository for storing data, a repository created through createStore that contains a number of methods to help us manage data:

  1. Dispatch: Distributes actions to change data in the warehouse

  2. GetState: Gets the state value currently stored in the repository

  3. ReplaceReducer: Replace the current reducer of the store (unchanged repository)

  4. Subscribe: registers a listener. The listener is a parameterless function that runs after the action is issued. Multiple listeners can be registered and triggered in the order they were registered; This function returns a function to cancel listening

    const store = createStore(reducer)
    // Returns a function to cancel listening
    const unSubscribe = store.subscribe(() = > {
        console.log('1. The state has changed ')
    })
    store.subscribe(() = > {
        console.log('2. State changes')})console.log('state: ', store.getState())
    // Assign a random action (for example)
    store.dispatch({
        type: 'SetLoginUser'.payload: {
            id: 1.username: 'K.'./* Other information */}})console.log(store.getState())
    Copy the code

    The printed result of the above code is:

    'state:' { loginUser: null.users: []}'1. The state has changed '
    '2. State changes'
    'state:' { loginUser: { id: 1.username: 'K.'. },users: []}Copy the code