Basic concepts of Redux
Applicable scenario
- Complex user interactions
- Under different permissions and roles
- Multi-user data sharing and collaboration
- Interact with the server more, using websocket
- 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
- Single data source: easy to manage and debug.
state
Read-only, ensuring that data changes can only be modified in one wayAction
reducer
Pure 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
createStore(reducer,[INIT_STATE],enhancer)
Initial creationredux
Container. Takes two arguments:reducer
Function, optionalINIT_STATE
forreducer
Parameters in a functionstate
Default value. This parameter is optionalenhancer
Compound higher-order function changesstore
Interface.reducer(state,action)
To deal withAction
, parameter: currentstate
To 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
-
GetState () returns the current state
-
Dispacth (Action) distributes acion. The argument takes a normal object and must contain the type field to indicate the type.
-
Subscribe (listener) Adds a listener. When state changes, the subscribe function listener is called
The return value is an unsubscribe function.
-
ReplaceReducer (nextReducer) replaces the current redcuer used to calculate state.
The middleware
applyMiddleWare(... middleWares)
Redux is extended with middle keys.- Each middleware accepts two parameters:
dispatch
和getState
. 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.
- The first parameter is zero
actionCreator
Or an object with the value actionCreator. - The second parameter is
store
The 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:
- create
reducer
, - create
store
. - Subscription functions.
- Gets the current latest
store
- Apply to the view UI.
- Data changes update the view.
- Gets the current latest
- 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
inReact
The 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.
- You can’t use
shouldComponentUpdate
To control component updates caused by data. - You can’t use
React.PureComponent
Create 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-redux
use
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
inclass
Use 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
- Prevents repeated rendering of components.
- 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