当前位置:网站首页>Study and exploration of Redux API implementation of Redux
Study and exploration of Redux API implementation of Redux
2022-07-29 09:19:00 【You Yudong】
learn redux
redux Application scenarios
- With JavaScript Single page application development is becoming more and more complex , Manage changing state Very difficult
- Redux To solve the problem state The data problem in
- stay React in , Data flows in one direction in a component
- Data flows from parent component to child component in one direction ( adopt props), Because of this feature , Two nonparental components ( Or brother components ) Communication between them is more troublesome
redux design idea
- Redux Is to store the whole application state in one place , be called store
- It contains a state tree state tree
- Components can be distributed dispatch Behavior action to store, Instead of informing other components directly
- Other components can subscribe to store The state of (state) To refresh your view
createStore
Use createStore Method can create a store, The argument to this function is reducer and initialState.
Three common methods :
- dispatch
- subscribe
- getState
Let's learn from the counter case createStore
Counter case
import { createStore } from "redux";
import { Component } from "react";
const ADD = "add";
const MINUS = "minus";
/**
* Pure function
* @param {*} state Old state
* @param {*} action action There has to be one type attribute
*/
function reducer(state = { counter: 0 }, action) {
switch (action.type) {
case ADD:
return { counter: state.counter + 1 };
case MINUS:
return { counter: state.counter - 1 };
default:
return state;
}
}
const store = createStore(reducer, { counter: 0 });
console.log(store);
class App extends Component {
state = {
// getState obtain store The state of
counter: store.getState().counter,
};
componentDidMount() {
// Changes in subscription status
store.subscribe(() => {
// Status update in the future Execute the callback
this.setState({
counter: store.getState().counter,
});
console.log(store.getState())
});
}
add = () => {
store.dispatch({ type: ADD });
};
minus = () => {
store.dispatch({ type: MINUS });
};
render() {
return (
<div>
<h2>counter: {this.state.counter}</h2>
<button onClick={this.add}>+1</button>
<button onClick={this.minus}>-1</button>
</div>
);
}
}
export default App;
createStore Implementation principle of
First : We need to create one createStore function , Receive two parameters : Pure function arguments reducer And initialization status initialState
/** * Create a store * @param {*} reducer Pure function The processor that calculates the new state * @param {*} initialState The initial state */
function createStore(reducer, initialState) {
// Define an initial state inside the warehouse
let state = initialState;
}
Next we need to define three functions : That's what we said above , Three commonly used functions .
function createStore(reducer, initialState) {
// Define an initial state inside the warehouse
let state = initialState;
/** * Get the latest status of the current warehouse */
function getState() {
return state;
}
/** * Update of subscription status * @param {*} listener The listening function executed after the status update * @returns Return a method that can cancel the listening function */
function subscribe(listener) {
}
/** * distributed Update status * @param {*} action action */
function dispatch(action) {
}
return {
getState,
dispatch,
subscribe,
};
}
getState Function is to return the latest current state , There's nothing to say .
dispatch Send an action to update the current state:
function dispatch(action) {
state = reducer(state, action);
}
subscribe The parameter of is a listening function , stay state After the value of is updated, call . So we need an array to record the saved listener, Then the return value of the function is a function that cancels the listening function .
// Monitor function
let listeners = [];
function subscribe(listener) {
listeners.push(listener);
// Return a method that can cancel the listening function
return () => {
listeners = listeners.filter((l) => l !== listener);
};
}
After the status update , We execute the listening function .
function dispatch(action) {
state = reducer(state, action);
// Execute listener function
listeners.forEach((listener) => listener());
}
At this time, we can put redux Replace it with this written by yourself createStore, It is found that the same effect can be achieved .
/** * There are two relationships between components and warehouses : * 1. Input Components can read state data from the warehouse for rendering and display * 2. Output You can send actions in the component , Modify the status in the warehouse */
const Counter = () => {
const [counter, setCounter] = useState(store.getState());
useEffect(() => {
const unsubscribe = store.subscribe(() => {
setCounter(store.getState());
});
return unsubscribe;
}, []);
return (
<div>
<h2>counter: {
counter.counter}</h2>
<button onClick={
() => store.dispatch({
type: ADD })}>+1</button>
<button onClick={
() => store.dispatch({
type: MINUS })}>-1</button>
</div>
);
};
Of course , Maybe we still don't think it's so elegant , If distributed action When , Just give the click button a function , Automatically distribute it for us , It's better not to need extra action .
bindActionCreators
Realize automatic distribution , We can use redux Provided bindActionCreators Method . The parameters of this method are actionCreator and dispatch Method .
const add = () => ({
type: ADD });
const minus = () => ({
type: MINUS });
const actionCreators = {
add, minus };
// Put one action Creator object and store.dispatch Method to bind Return an object
const boundActions = bindActionCreators(actionCreators, store.dispatch);
// ....
<button onClick={
boundActions.add}>+1</button>
<button onClick={
boundActions.minus}>-1</button>
In this way, automatic distribution can be realized action 了 .
that :bindActionCreators How is it realized ?
In fact, it is to encapsulate the distributed function ,actionCreator That's right action Encapsulation , The execution result of each function of this object is a action object , Then pass the object to dispatch The method can .
/** * binding actionCreator and dispatch Realize automatic distribution * @param {*} actionCreator * @param {*} dispatch */
function bindActionCreators(actionCreator, dispatch) {
// Bound automatic dispatch object
const boundActionCreators = {
};
for (const key in actionCreator) {
const ac = actionCreator[key];
// Bound function
boundActionCreators[key] = bindActionCreator(ac, dispatch);
}
return boundActionCreators;
}
function bindActionCreator(actionCreator, dispatch) {
return (...args) => {
// 、 distributed action You can also receive incoming parameters
return dispatch(actionCreator(...args));
};
}
combineReducers
React No matter how big the application is , There can only be one warehouse , There can only be one reducer,reducer Only one state can be maintained . That may lead to one of us reducer There are hundreds inside switch case, It's hard to maintain .
therefore : We can consider putting reducer Split , A unique reducer, By many small reducer It's a combination of . It is divided into modules .
action-type
export const ADD = "add";
export const ADDNUM = "addNum";
export const MINUS = "minus";
export const ADD2 = "add2";
export const ADDNUM2 = "addNum2";
export const MINUS2 = "minus2";
counter1 reducer:
import {
ADD, MINUS, ADDNUM } from "../action-type";
/** * Pure function * @param {*} state Old state * @param {*} action action There has to be one type attribute */
function reducer(state = {
counter: 0 }, action) {
switch (action.type) {
case ADD:
return {
counter: state.counter + 1 };
case ADDNUM:
return {
counter: state.counter + action.num };
case MINUS:
return {
counter: state.counter - 1 };
default:
return state;
}
}
export default reducer;
counter2 reducer
import {
ADD2, MINUS2, ADDNUM2 } from "../action-type";
function reducer(state = {
counter: 0 }, action) {
switch (action.type) {
case ADD2:
return {
counter: state.counter + 1 };
case ADDNUM2:
return {
counter: state.counter + action.num };
case MINUS2:
return {
counter: state.counter - 1 };
default:
return state;
}
}
export default reducer;
Merge reducer: With the help of redux Provided combineReducers Method
import counter1 from "./counter1";
import counter2 from "./counter2";
import {
combineReducers } from "redux";
// Merge multiple reducer
const reducers = {
counter1, counter2 };
const combineReducer = combineReducers(reducers);
export default combineReducer;
import { createStore } from "../redux";
import combineReducer from "./reducers";
const store = createStore(combineReducer);
export default store;
export * from "./action-type";
actionCreators:
import {
ADD, MINUS, ADDNUM } from "../action-type";
// actionCreators
const add = () => ({
type: ADD });
const addNum = (num) => ({
type: ADDNUM, num });
const minus = () => ({
type: MINUS });
const actionCreators = {
add, addNum, minus };
export default actionCreators;
import {
ADD2, MINUS2, ADDNUM2 } from "../action-type";
// actionCreators
const add2 = () => ({
type: ADD2 });
const addNum2 = (num) => ({
type: ADDNUM2, num });
const minus2 = () => ({
type: MINUS2 });
const actionCreators2 = {
add2, addNum2, minus2 };
export default actionCreators2;
There is no interference between the two counters :
import store from "./store";
import { useEffect, useState } from "react";
import { bindActionCreators } from "./redux";
import actionCreators from "./store/actionCreator/counter";
import actionCreators2 from "./store/actionCreator/counter2";
// Put one action Creator object and store.dispatch Method to bind Return an object
const boundActions = bindActionCreators(actionCreators, store.dispatch);
/**
* There are two relationships between components and warehouses :
* 1. Input Components can read state data from the warehouse for rendering and display
* 2. Output You can send actions in the component , Modify the status in the warehouse
*/
const Counter = () => {
const [counter, setCounter] = useState(store.getState().counter1);
useEffect(() => {
const unsubscribe = store.subscribe(() => {
setCounter(store.getState().counter1);
});
return unsubscribe;
}, []);
return (
<div>
<h2>counter: {counter.counter}</h2>
<button onClick={boundActions.add}>+1</button>
<button onClick={() => boundActions.addNum(5)}>+5</button>
<button onClick={boundActions.minus}>-1</button>
</div>
);
};
const boundActions2 = bindActionCreators(actionCreators2, store.dispatch);
const Counter2 = () => {
const [counter, setCounter] = useState(store.getState().counter2);
useEffect(() => {
const unsubscribe = store.subscribe(() => {
setCounter(store.getState().counter2);
});
return unsubscribe;
}, []);
return (
<div>
<h2>counter: {counter.counter}</h2>
<button onClick={boundActions2.add2}>+1</button>
<button onClick={() => boundActions2.addNum2(5)}>+5</button>
<button onClick={boundActions2.minus2}>-1</button>
</div>
);
};
const App = () => {
return (
<>
<Counter />
<hr />
<Counter2 />
</>
);
};
export default App;
combineReducers Principle
The method is to combine multiple reducer For one reducer. Parameters are one or more reducer The object of the collection , And for many reducer Before, it was divided into modules , States are also divided into multiple modules , The module name is reducer Collection of objects key.
/** * Merge multiple reducer * @param {
{[key:string]:Function}} reducers {counter1: reducer1 } */
export function combineReducers(reducers) {
// Returns a globally unique reducer function state It is the last time of each incoming state
return function (state = {
}, action) {
let nextState = {
};
for (const key in reducers) {
const reducer = reducers[key];
// Old state
const lastStateForKey = state[key];
// Calculate the new state
nextState[key] = reducer(lastStateForKey, action);
}
return nextState;
};
}
边栏推荐
- Asp graduation project - based on C # +asp Design and implementation of enterprise investment value analysis system based on. Net + sqlserver (graduation thesis + program source code) -- enterprise in
- ERROR 1045 (28000): Access denied for user ‘ODBC‘@‘localhost‘ (using password: NO)
- Sublime text create page
- Restful style details
- Floweable foundation Chapter 1
- Summary of research on endogenous information security technology of industrial measurement and control equipment
- Discussion on the integration of storage and calculation and the calculation in storage
- Opencv cvcircle function
- Trie树(字典树)讲解
- The gold content of PMP certificate has been increased again and included in the scope of Beijing work residence permit
猜你喜欢
数据表示与计算(进制)

Asp graduation project - based on C # +asp Net+sqlserver laboratory reservation system design and Implementation (graduation thesis + program source code) - Laboratory Reservation System

English high frequency suffix

云原生管理实践:业务引领的DevOps持续交付体系

mysql将部分表名统一转换为大写

The gold content of PMP certificate has been increased again and included in the scope of Beijing work residence permit

Safety is no longer the only selling point. Test drive "slash youth" Volvo C40

Qmainwindow details

No swagger, what do I use?

How does xjson implement four operations?
随机推荐
redis可视化工具读取数据乱码问题解决
Solve the problem of false Base64 character in Base64
User identity identification and account system practice
What are the backup and recovery methods of gbase 8s database
机器学习之分类模型评估指标及sklearn代码实现
基于C语言实现的NFA确定化和DFA最小化
LeetCode刷题(6)
用户身份标识与账号体系实践
Collation of ml.net related resources
【云驻共创】【HCSD大咖直播】亲授大厂面试秘诀
Shutter gradient
2022 R2 mobile pressure vessel filling test question simulation test platform operation
Information system project manager must recite the quality grade of the core examination site (53)
Data representation and calculation (base)
QMainWindow 详解
怎样查询快递物流筛选出无信息单号删除或者复制
2022电工(初级)考题模拟考试平台操作
GBase 8s数据库有哪些备份恢复方式
201803-3 Full Score solution of CCF URL mapping
Quick sorting (quick sorting) (implemented in C language)