Basic concepts of Redux

Applicable scenario

  1. Complex user interactions
  2. Under different permissions and roles
  3. Multi-user data sharing and collaboration
  4. Interact with the server more, using websocket
  5. Pages require data from multiple sources

Basic concept

store

There can only be one container object store for data management in the entire application

state

Contains all data at the current time. Get it from store.getState()

action

Use to issue notifications that change state. Issue actions via store.dispatch()

reducer

Used to handle Action notifications that are issued. You need to return a brand new state

Reducer must be a pure function. The same input must yield the same output.

Design concept

  1. Single data source: easy to manage and debug.
  2. stateRead-only, ensuring that data changes can only be modified in one wayAction
  3. reducerPure functions handle data updates

Common API definitions

creatStore
import {createStore,combineReducers} from 'redux'
// reducer
function userReducer(state=INIT_STATE.userInfo,action) {
    switch(action.type){
        case 'update':
            return{... state, ... action.payload }case 'delete':
            return action.payload
        default:
            return state
    }
}
/ / create
const store = createStore(reducer)
const {subscribe,dispatch,getState} = store
Copy the code
  1. createStore(reducer,[INIT_STATE],enhancer)Initial creationreduxContainer. Takes two arguments:reducerFunction, optionalINIT_STATEforreducerParameters in a functionstateDefault value. This parameter is optionalenhancerCompound higher-order function changesstoreInterface.
  2. reducer(state,action)To deal withAction , parameter: currentstateTo be dealt withaction
combindReducers

Merge multiple reducer parameters into one object

import {createStore,combineReducers} from 'redux'
Reducer of multiple modules
import * as UserReducer from './reducers/User'
import * as LogReducer from './reducers/Log'

Merge the reducer into a whole
const reducer = combineReducers({UserReducer,LogReducer})
/ / create
const store = createStore(reducer)
Copy the code
Data container objectStore
  1. GetState () returns the current state

  2. Dispacth (Action) distributes acion. The argument takes a normal object and must contain the type field to indicate the type.

  3. Subscribe (listener) Adds a listener. When state changes, the subscribe function listener is called

    The return value is an unsubscribe function.

  4. ReplaceReducer (nextReducer) replaces the current redcuer used to calculate state.

The middleware
  1. applyMiddleWare(... middleWares)Redux is extended with middle keys.
  2. Each middleware accepts two parameters:dispatchgetState. Returns a functionnext(action)Used to manually call the next call chain (used to change the default initiated aciton, default automatic call if not changed). The last call chain will receive the actual realdispatch .
bindActionCreators(actionCreators,dispatch)

Format the way in which classes such as store.dispatch(updateUser()) can be wrapped so that the updateUser() call can be called directly in the component.

  1. The first parameter is zeroactionCreatorOr an object with the value actionCreator.
  2. The second parameter isstoreThe distribution function provided.

componse(... fun)

Combine multiple functions from right to left. Run in order

Redux basic use

The installation

npm install redux --save
Copy the code

Based on the sample

Step by step:

  1. createreducer ,
  2. createstore.
  3. Subscription functions.
    1. Gets the current lateststore
    2. Apply to the view UI.
    3. Data changes update the view.
  4. Event distribution.
import {createStore} from 'redux'

/ / the initial state
const INIT_STATE = {
    userInfo: {name:'admin'.age:20,}}// reducer
function userReducer(state=INIT_STATE.userInfo,action) {
    switch(action.type){
        case 'update':
            return{... state, ... action.payload }case 'delete':
            return action.payload
        default:
            return state
    }
}

// init
const store = createStore(userReducer)

// Subscribe to change
// Prints the data when it changes
store.subscribe(() = >console.log(store.getState()))

/ / distribution of the action
store.dispatch({type:'update'.payload: {name:'test'}})
store.dispatch({type:'update'.payload: {name:'test'.age:32}})
store.dispatch({type:'delete'})
store.dispatch({type:'add'})

Copy the code

Manage multiplereducer

Depending on the volume and complexity of the business, having only one reducer processing function becomes extremely large and difficult to maintain.

By splitting modules and dividing functions. Create multiple reducer to manage data state of individual modules.

Userinfo.js for user data management, data update;

// userInfo.js
const INIT_STATE = {
    name:'admin'.age:23.address:Nanjing, Jiangsu Province
}

export function UserInfo(state=INIT_STATE,action) {
    const {type,data} = action
    switch(type){
        case 'updateName':
            return {
                ...state,
                name:data,
            }
        case 'updateAge':
            return {
                ...state,
                age:data,
            }
        default:
            return state
    }
}
Copy the code

Authinfo. js is used for the current login user’s permission information, permission update;

// authInfo.js
const INIT_STATE = {
    role:'Administrator'.enable:false.operations:[],
}
export function AuthInfo(state=INIT_STATE,action) {
    
    const {type,data} = action
    switch(type){
        case 'updateRole':
            return {
                ...state,
                role:data,
            }
        case 'addOperate':
            // Copy, deep copy if necessary
            const obj = Object.assign(state)
            obj.operations.push(data)
            return obj
        default:
            return state
    }
}

Copy the code

Reducers. Js redux initializes the main entry file and summarizes the reducer in the module;

// reducers.js
import {combineReducers} from 'redux'
// Import module Reducer
import {UserInfo as user} from './UserInfo'
import {AuthInfo as auth} from './AuthInfo'

const reducers = combineReducers({
    user,
    auth,
})

export default reducers
// index.js
import {createStore} from 'redux'

import reducer from './reducers'
// init
const store = createStore(reducer)

// Subscribe to change
// Prints the data when it changes
store.subscribe(() = >console.log(store.getState()))
store.dispatch({type:'INIT_STATE'})
store.dispatch({type:'updateName'.data:'test'})
store.dispatch({type:'updateRole'.data:'Test member'})
Copy the code

inReactThe use of

Without react-redux, access store;

Index.js project main entry file;

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// store
import store from './store'

const render = () = >ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>.document.getElementById('root'));// Store changes to re-render
render()
store.subscribe(render)

Copy the code

ReduxBox /index.js simulates a function module and retrieves global store data.

// reduxBox/index.js
import { lazy } from "react";
import { useRouteMatch, Route, Redirect } from "react-router-dom";
// Import store data directly
import store from '.. /store/index'
// Component load
const UserInfo = lazy(() = >import("./userInfo"))

export default function App(props) {
  const { url } = useRouteMatch();
  const {user,auth} = store.getState();
  return (
    <>
      <div>
        <p>Store the data</p>
        <p>Name :{user.name} - age :{user.age} - address :{user.address}</p>
        <p>Role Name :{auth.role} - Valid or not :{auth.enable} - Operation permission :{auth.operations}</p>
      </div>
      <Route exact path={url}>
        <Redirect to={` ${url} /userInfo`} / >
      </Route>
      <Route path={` ${url} /userInfo`} component={UserInfo} />
    </>
  );
}
Copy the code

ReduxBox/userinfo.js user information module, display user information; User information can be updated

// reduxBox/userInfo.js
import {Input} from 'antd'
import { useState } from 'react'
// Import store data directly
import store from '.. /store/index'

export default function UserInfo(props) {
    const [name,setName] = useState(' ')
    // Get data
    const {user} = store.getState();

    return (<div style={{border:'1px solid #fff'}} >
        <p>The current state</p>
        <p>Name :{user.name} - age :{user.age} - address :{user.address}</p>

        <Input onChange={e= >setName(e.target.value)} onPressEnter={e=>store.dispatch({type:'updateName',data:name})} />
    </div>)}Copy the code

With Input confirmation, you can see that both the component itself and the parent component’s data view have been updated. There doesn’t seem to be any problem with that;

The main reason it works is because the store data changes and the whole project is re-rendered to get the latest data.

// Initialize render
render()
// store subscribes to updates, called when data changes
// reactdomo.render () is called repeatedly to update the DOM map to the latest element if necessary.
store.subscribe(render)
Copy the code

The obvious defect is that store data is not integrated into React at all. It is not a part of React. In our actual business, what caused the component update caused a lot of trouble.

  1. You can’t useshouldComponentUpdateTo control component updates caused by data.
  2. You can’t useReact.PureComponentCreate a component because the component itself never gets a reason to update.

React-redux uses the context to connect redux to react, as the functions are limited (they can only be transmitted from parent to child). Map the data to the props of the component

You can use context to make store data change and cause views to change more like react style

// store
import store from './store'
/ / create the context
export const StoreContext = React.createContext({})

const render = () = >ReactDOM.render(
  <React.StrictMode>
    <StoreContext.Provider value={store.getState()}>
      <App />
    </StoreContext.Provider>
  </React.StrictMode>.document.getElementById('root'));// Store changes to re-render
render()
// store.subscribe(render)
Copy the code

There is no need to store. Subscribe (render) to subscribe updates.

But using the context still doesn’t give us much control over the component’s data, even though it’s a bit react style, hence the React-Redux

react-reduxuse

The installation

npm install --save react-redux
Copy the code

Data changes that cause component updates :state, props, context

The API definition

Provider

Attach the Redux Store to the entire application layer, the class component uses connect to get data and forward actions, and the function component uses hooks useSelector\useDispatch

import {Provider} from 'react-redux'
// store
import store from './store'
/ / create the context
// export const StoreContext = React.createContext({})

const render = () = >ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>.document.getElementById('root')); render()Copy the code

Used in function componentshooks

useSelector

Get the data in store.

useDispatch

Event distribution function.

Code example description:

// react-redux
import {useSelector,useDispatch} from 'react-redux'
export default function UserInfo(props) {
    const [name,setName] = useState(' ')
    // Get data
    const {user} = useSelector(state= >{
        const {user} = state
        return {
            user
        }
    })
    // Forwarding events
    const dispatch = useDispatch()
    return (<div style={{border:'1px solid #fff'}} >
        <p>The current state</p>
        <p>Name :{user.name} - age :{user.age} - address :{user.address}</p>

        <Input onChange={e= >setName(e.target.value)} onPressEnter={e=>dispatch({type:'updateName',data:name})} />
    </div>)}Copy the code

inclassUse in components

connect(mapStateToProps,mapDispatchToProps,mergeProps,options)

Link the React component to the ReduxStore

* 'mapStateToProps(gloablState,ownProps)' link 'to' store ', * 'mapDispatchToProps(Dispatch,ownProps)' defines action Creator, By calling, initiate action * ` mergeProps ` (stateProps, dispatchProps, ownProps) By default return ` Object. The assign ({}, wonProps stateProps, dispatchProps) ` * ` options = {pure: Boolean, withRef: Boolean} ` ` pure ` Shallow comparison of the results of mergeProps to avoid unnecessary updates;Copy the code

BindActionCreators (fn | object: {fn}) into action creator, reduce model reuse;

/ / in the sample
dispatch({type:'addOperate'.data:values})
/ / conversion
const updateOperates = bindActionCreators((values) = >{
  return {
        type:'addOperate'.data:values,
    }
},dispatch)
// Then call
updateOperates()
Copy the code

Sample code:

/** * Role information */
import React from 'react'
import {Select} from 'antd'
import {connect} from 'react-redux'
const {Option} = Select

class RoleInfo extends React.Component{
    render(){
        // Get data from Props
        const {auth,dispatch} = this.props
        
        return(<div style={{border:'1px solid #fff'}} >
            <p>The current state</p>
            <p>Role Name :{auth.role} - Valid or not :{auth.enable} - Operation permission :{auth.operations}</p>

            <Select style={{width:100}} mode="multiple" onChange={values= >dispatch({type:'addOperate',data:values})}>
                <Option value='1'>add</Option>
                <Option value='2'>delete</Option>
                <Option value='3'>Modify the</Option>
            </Select>
        </div>)}}export default connect(
    state= >{
        const {auth} = state
        return {
            auth,
        }
    },
    dispatch= >{
        return {
            dispatch,
        }
    }
)(RoleInfo)
Copy the code

Performance optimization

  1. Prevents repeated rendering of components.
  2. Double computation of derived data.

The resources

The story’s official website

Redux Chinese website

The react – the story’s official website

Ruan Yifeng’s Redux tutorial

Redux official video tutorial