React(三)

Redux
  • 纯函数

    • 函数在相同的输入值时,产生相同的输出
    • 函数的输出和输入值以外的其他隐藏信息或状态无关,也由I/O设备产生的外部输出无关
    • 函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等
    • 也就是
      • 确定的输入,一定会产生确定的输出
      • 函数在执行过程中,不能产生副作用
  • 文件构成

    1
    2
    3
    4
    5
    // store/index.js
    const { createStore } = require("redux");
    const { reducer } = require("./reducer");
    const store = createStore(reducer);
    module.exports = store;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // store/reducer.js
    const { CHANGE_NAME } = require("./store/constants");

    // 初始化数据
    const initialState = {
    name: "cy",
    age: "18",
    };

    // 定义reducer函数:纯函数
    function reducer(state = initialState, action) {
    switch (action.type) {
    case CHANGE_NAME:
    return { ...state, name: action.name };
    default:
    return state;
    }
    }

    module.export = {
    reducer,
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // store/actionCreators.js
    const { CHANGE_NAME } = require("./store/constants");

    // actionCreators:创建action
    const changeNameAction = (name) => ({
    type: CHANGE_NAME,
    name,
    });

    module.exports = {
    changeNameAction,
    };

    1
    2
    3
    4
    5
    6
    // store/constants.js
    const CHANGE_NAME = "change_name";

    module.exports = {
    CHANGE_NAME,
    };
  • 三大原则

    • 单一数据源
      • 整个应用程序的state被存储在一颗object tree中,并且这个object tree只存储在一个store中
        • 可以创建多个store,但是并不利于数据的维护
      • 单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改
    • state是只读的
      • 唯一修改state的方法一定是触发action,不要试图在其他地方通过任何的方法来修改state
      • 确保view或者网络请求都不能直接修改state,它们只能通过action来描述自己想要如何修改
      • 保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所有不需要担心race condition (竞态) 的问题
    • 使用纯函数来执行修改
      • 通过reducer将旧state和actions联系在一起,并且返回一个新的state
      • 随着应用程序的复杂度增加,我们可以将reducer拆分成多个小的reducer,分别操作不同state tree 的一部分
      • 但是所有的reducer都应该是纯函数,不能产生任何的副作用
  • node中对ES6模块化的支持

    • 从node v13.2.0开始,node才对ES6模块化提供了支持
    • node v13.2.0之前,需要进行如下操作:
      • package.json中添加属性:“type":“module”;
      • 在执行命令后添加如下选项: node --experimental-modules src/index.js
    • node v13.2.0之后,只需要
      • package.json中添加属性:”type":“module”
  • Redux Toolkit

    • configureStore:包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合你的slice reducer,添加你提供的任何Redux中间件,redux-thunk默认包含,并启用Redux DevTools Extension
    • createSlice:接收一个reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions
    • createAsyncThunk:接受一个动作类型字符串和一个返回承诺的函数,并生成一个pending/fulfilled/rejected基于该承诺分派动作类型的thunk
      • createAsyncThunk创建出来的action被dispatch的时候,会有三种状态
        • pending:action被发出,但是还没有最终的结果
        • fulfilled:获取到最终的结果
        • rejected:执行过程中有错误或者抛出了异常
  • 自定义connect函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// hoc/index.js
export { storeContext } from "./storeContext";
export { connect } from "./connect";

// hoc connect.js

import { PureComponent } from "react";
import { storeContext } from "./storeContext";
/**
*
* @param {Fn} mapStateToProps
* @param {Fn} mapDispatchToProps
* return Fn
*/
export function connect(mapStateToProps, mapDispatchToProps) {
// 返回的函数是个高阶函数
return function (WrapperComponent) {
class NewComponent extends PureComponent {
constructor(props, context) {
super(props);

this.state = mapStateToProps(context.getState());
}

componentDidMount() {
this.unsubscribe = this.context.subscribe(() => {
this.setState(mapStateToProps(this.context.getState()));
});
}

componentWillUnmount() {
this.unsubscribe();
}

render() {
const stateObj = mapStateToProps(this.context.getState());
const dispatchObj = mapDispatchToProps(this.context.dispatch);

return (
<WrapperComponent {...this.props} {...stateObj} {...dispatchObj} />
);
}
}

NewComponent.contextType = storeContext;
return NewComponent;
};
}

// hoc/storeContext.js
import { createContext } from "react";

export const storeContext = createContext();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
App.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { Provider } from "react-redux";
import { storeContext } from "./hoc/storeContext.js";
import store from "./store/index.js";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<storeContext.Provider value={store}>
<App />
</storeContext.Provider>
</Provider>
</React.StrictMode>
);

自定义redux-thunk

1
2
3
4
5
6
7
8
9
10
11
12
13
function thunk(store){
const next = store.dispatch
function dispatchThunk(action) {
if(typeof action === 'function') {
action(store.dispatch)
}else{
next(action)
}
}
store.dispatch = dispatchThunk
}

thunk(store)

合并中间件

1
2
3
4
5
6
7
8
9
function applyMiddleware(store,middlewares) {
middlewares = middlewares.slice()

middlewares.forEach(middleware => {
store.dispatch = middleware(store)
})
}

applyMiddleware(store,[patchLogging,patchThunk])