redux知识点总结

一. redux 核心

1.1. redux 入门

1.1.1 state

用于数据存储

1
2
3
const initialState = {
count: 0
};

1.1.2. action

通过 action 来更新 state 中的数据

  • 所有数据的变化,必须通过派发(dispatch)action 来更新
  • action 是一个普通的 JavaScript 对象,用来描述这次更新的 type 和 content

优势与用法

  • 使用 action 可以清晰的知道数据所发生的变化,所有的数据变化都是可追踪、可预测的
  • 目前的 action 是固定的对象,实际开发中,会通过函数来定义,返回一个 action
1
2
const addCounterAction = { type: "ADD_COUNTER", num: 6 };
const subCounterAction = { type: "SUB_COUNTER", num: 2 };

1.1.3. reducer

将 state 和 action 联系在一起

  • reducer 是一个纯函数
  • reducer 做的事情就是将传入的 state 和 action 结合起来生成一个新的 state

1.1.4. redux 三大原则

单一数据源

  • 整个应用程序的 state 被存储在一棵 object tree 中,并且这个 object tree 只存储在一个 store 中
  • redux 并没有强制不能创建多个 store,但是那样做并不利于数据的维护
  • 单一的数据源可以让整个应用程序的 state 变得方便维护、追踪、修改

state 是只读的

  • 唯一修改 State 的方法一定是触发 action,不要试图在其他地方通过任何的方式来修改 state
  • 这样就确保了 View 或网络请求都不能直接修改 state,只能通过 action 来修改 state
  • 这样可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心 race condition(竟态)的问题

使用纯函数来执行修改

  • 通过 reducer 将旧 state 和 actions 联系在一起,并且返回一个新的 state
  • 随着应用程序的复杂度增加,可以将 reducer 拆分成多个小的 reducers,分别操作不同 state tree 的一部分
  • 但是所有的 reducer 都应该是纯函数,不能产生任何的副作用

1.2. redux 基本使用

1.2.1. redux 安装与配置

安装

1
npm install redux

ES6 模块化配置

  • node v13.2.0 之前,需要进行如下操作
    • 在 package.json 中添加属性: “type”: “module”
    • 在执行命令中添加如下选项:node –experimental-modules src/index.js
  • node v13.2.0 之后,只需要进行如下操作
    • 在 package.json 中添加属性: “type”: “module”
  • 注意:导入文件时,需要跟上.js 后缀名

1.2.2. redux 使用过程

1.创建一个对象,作为要保存的状态

1
2
3
4
5
const { createStore } = require("redux");

const initialState = {
count: 0
};

2.创建 store 来存储这个 state

  • 创建 store 时必须创建 reducer
  • 通过 store.getState 来获取当前的 state
1
2
3
4
5
6
7
const reducer = (state = initialState, action) => {
return state;
};

const store = createStore(reducer);

const state = store.getState();

3.通过 action 来修改 state

  • 通过 dispatch 来派发 action
  • 通常 action 中都会有 type 属性,也可以携带其他的数据
1
2
3
4
5
const addCounterAction = { type: "ADD_COUNTER", num: 6 };
const subCounterAction = { type: "SUB_COUNTER", num: 2 };

store.dispatch(addCounterAction);
store.dispatch(subCounterAction);

4.修改 reducer 中的处理代码

  • reducer 是一个纯函数,不需要直接修改 state
1
2
3
4
5
6
7
8
9
10
const reducer = (state = initialState, action) => {
switch (action.type) {
case: "ADD_COUNTER":
return { ...state, count: state.count + action.num }
case: "SUB_COUNTER":
return { ...state, count: state.count - action.num }
default:
return state
}
}

5.在派发 action 之前,监听 store 的变化

1
2
3
4
store.subscribe(() => {
const state = store.getState();
console.log(state);
});

6.完整实现

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
const { createStore } = require("redux")

const initialState = {
count: 0
}

const reducer = (state = initialState, action) => {
switch (action.type) {
case: "ADD_COUNTER":
return { ...state, count: state.count + action.num }
case: "SUB_COUNTER":
return { ...state, count: state.count - action.num }
default:
return state
}
}

const store = createStore(reducer)

store.subscribe(() => {
const state = store.getState()
console.log(state)
})

const addCounterAction = { type: "ADD_COUNTER", num: 6 }
const subCounterAction = { type: "SUB_COUNTER", num: 2 }

store.dispatch(addCounterAction)
store.dispatch(subCounterAction)

1.2.3. redux 结构划分

store/index.js 文件

1
2
3
4
5
6
import { createStore } from "redux";
import reducer from "./reducer.js";

const store = createStore(reducer);

export default store;

store/reducer.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { ADD_COUNTER, SUB_COUNTER } from "./constants.js"

const initialState = {
count: 0
}

const reducer = (state = initialState, action) => {
switch (action.type) {
case: ADD_COUNTER:
return { ...state, count: state.count + action.num }
case: SUB_COUNTER:
return { ...state, count: state.count - action.num }
default:
return state
}
}

export default reducer

store/actionCreators.js 文件

1
2
3
4
5
6
import { ADD_COUNTER, SUB_COUNTER } from "./constants.js";

const addCounterAction = num => ({ type: ADD_COUNTER, num });
const subCounterAction = num => ({ type: SUB_COUNTER, num });

export { addCounterAction, subCounterAction };

store/constants.js 文件

1
2
3
4
const ADD_COUNTER = addCounter;
const SUB_COUNTER = subCounter;

export { ADD_COUNTER, SUB_COUNTER };

1.2.4. redux 流程解析

image-20230410095424177

image-20230410095601266

1.2.5. redux 在 react 中的使用

views/Home.jsx

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
import React, { PureComponent } from "react";

import store from "../store";
import { addCounterAction, subCounterAction } from "../store/actionCreators";

export class Home extends PureComponent {
constructor(props) {
super(props);

this.state = {
count: store.getState().count
};
}

componentDidMount() {
store.subscribe(() => {
this.setState({
count: store.getState().count
});
});
}

addCounter(num) {
store.dispatch(addCounterAction(num));
}

subCounter(num) {
store.dispatch(subCounterAction(num));
}

render() {
const { count } = this.state;
return (
<div>
<h1>Home</h1>
<h2>当前计数: {count}</h2>
<button onClick={e => this.addCounter(2)}>+2</button>
<button onClick={e => this.subCounter(1)}>-1</button>
</div>
);
}
}

export default Home;

1.3. react-redux

安装

1
npm install react-redux

connect 函数

views/Home.jsx

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
import React, { PureComponent } from 'react'
import { connect } from "react-redux"
import {
addCounterAction,
subCounterAction
} from '../store/actionCreators'

export class Home extends PureComponent {
addCounter(num) {
const { addCounter } = this.props
addCounter(num)
}

subCounter(num) {
const { subCounter } = this.props
subCounter(num)
}

render() {
const { count } = this.props
return (
<div>
<h1>Home</h1>
<h2>当前计数: {count}</h2>
<button onClick={e => this.addCounter(2)}>+2</button>
<button onClick={e => this.subCounter(1)}>-1</button>
</div>
)
}
}

const mapStateToProps = (state) => ({count: state.count})

const mapDispatchToProps = (dispatch) => ({
addCounter: (num) => dispatch(addCounterAction(num))
subCounter: (num) => dispatch(subCounterAction(num))
})

export default connect(mapStateToProps, mapDispatchToProps)(Home)

Provider

  • 注意:这里传入的是 store 属性,而不是 value 属性

src/index.js

1
2
3
4
5
6
7
8
9
10
11
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import App from "./App";
import store from "./store";

const root = ReactDOM.createRoot(document.querySelector("#root"));
root.render(
<Provider store={store}>
<App />
</Provider>
);

1.4. redux 异步操作

注意:以下示例代码,使用 react-redux 处理,并省略 Provide 的配置

1.4.1. redux 配置

reducer.js

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
import {
...,
FETCH_BANNERS,
FETCH_RECOMMENDS
} from "./constants.js"

const initialState = {
...,
banners: [],
recommends: []
}

const reducer = (state = initialState, action) => {
switch (action.type) {
...
case: FETCH_BANNERS:
return { ...state, banners: action.data }
case: FETCH_RECOMMENDS:
return { ...state, recommends: action.data }
default:
return state
}
}

export default reducer

store/actionCreators.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {
...,
FETCH_BANNERS,
FETCH_RECOMMENDS
} from "./constants.js"

...
const fetchBannersAction = (data) => ({ type: FETCH_BANNERS, data })
const fetchRecommendsAction = (data) => ({ type: FETCH_RECOMMENDS, data })

export {
...,
fetchBannersAction,
fetchRecommendsAction
}

store/constants.js 文件

1
2
3
4
5
6
7
8
9
...
const FETCH_BANNERS = fetchBanners
const FETCH_RECOMMENDS = fetchRecommends

export {
...,
FETCH_BANNERS,
FETCH_RECOMMENDS
}

1.4.2. 在组件中请求数据

views/Detail

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
54
55
56
57
58
59
import React, { PureComponent } from "react";
import { connect } from "react-redux";
import axios from "axios";
import {
fetchBannersAction,
fetchRecommendsAction
} from "../store/actionCreators";

export class Detail extends PureComponent {
componentDidMount() {
const { fetchBanners, fetchRecommends } = this.props;

axios
.get("url")
.then(res => {
const banners = res.data.data.banner.list;
const recommends = res.data.recommend.list;

fetchBanners(banners);
fetchRecommends(recommends);
})
.catch(err => {
console.log(err);
});
}

render() {
const { banners, recommends } = this.props;
return (
<div>
<h1>Datail</h1>
<h2>banners: </h2>
<div>
{banners.map(item => (
<div key={item}>{item}</div>
))}
</div>
<h2>recommends: </h2>
<div>
{recommends.map(item => (
<div key={item}>{item}</div>
))}
</div>
</div>
);
}
}

const mapStateToProps = state => ({
banners: state.banners,
recommends: state.recommends
});

const mapDispatchToProps = dispatch => ({
fetchBanners: data => dispatch(fetchBannersAction(data)),
fetchRecommends: data => dispatch(fetchRecommendsAction(data))
});

export default connect(mapStateToProps, mapDispatchToProps)(Detail);

1.4.3. 在 action 中请求数据

1.4.3.1. 中间件

redux 也引入了中间件(Middleware)的概念

  • 中间件的目的是在 dispatch 的 action 和最终达到的 reducer 之间,扩展一些自己的代码
  • 比如日志记录、调用异步接口、添加代码调试功能等等
  • 官网推荐的中间件是使用 redux-thunk

redux-thunk 发送异步请求原理

  • 默认情况下的 dispatch(action),action 需要是一个 JavaScript 的对象

  • redux-thunk 可以让 dispatch(action 函数),action 可以是一个函数

  • 该函数会被调用,并且会传给这个函数一个 dispatch 函数和 getState 函数

    • dispatch 函数用于我们之后再次派发 action

    • getState 函数考虑到之后的一些操作需要依赖原来的状态,可以获取之前的一些状态

异步处理流程

image-20230410115636366

1.4.3.2. redux-thunk

redux-thunk 的安装

1
npm install redux-thunk

使用过程

  • 在创建 store 时传入应用了 middleware 的 enhance 函数
    • 通过 applyMiddleware 来结合多个 Middleware, 返回一个 enhancer
    • 将 enhancer 作为第二个参数传入到 createStore 中
1
2
3
4
5
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";

const enhancer = applyMiddleware(thunk);
const store = createStore(reducer, enhancer);
  • 定义返回一个函数的 action
    • 注意:这里不是返回一个对象了,而是一个函数
    • 该函数在 dispatch 之后会被执行
1
2
3
4
5
6
7
8
9
10
11
const getDataAction = () => {
return dispatch => {
axios.get("url").then(res => {
const banners = res.data.data.banner.list;
const recommends = res.data.recommend.list;

dispatch(fetchBanners(banners));
dispatch(fetchRecommends(recommends));
});
};
};
1.4.3.3. 完整实现

store/index.js 文件

1
2
3
4
5
6
7
8
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer.js";

const enhancer = applyMiddleware(thunk);
const store = createStore(reducer, enhancer);

export default store;

store/actionCreators.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
const fetchDataAction = () => {
return (dispatch) => {
axios.get("url").then(res => {
const banners = res.data.data.banner.list
const recommends = res.data.recommend.list

dispatch(fetchBanners(banners))
dispatch(fetchRecommends(recommends))
}).catch(err => {
console.log(err)
})
}
}

export {
...,
fetchDataAction
}

views/Detail.jsx

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
import React, { PureComponent } from "react";
import { connect } from "react-redux";
import { fetchDataAction } from "../store/actionCreators";

export class Detail extends PureComponent {
componentDidMount() {
const { fetchData } = this.props;
fetchData();
}

render() {
const { banners, recommends } = this.props;
return (
<div>
<h1>Datail</h1>
<h2>banners: </h2>
<div>
{banners.map(item => (
<div key={item}>{item}</div>
))}
</div>
<h2>recommends: </h2>
<div>
{recommends.map(item => (
<div key={item}>{item}</div>
))}
</div>
</div>
);
}
}

const mapStateToProps = state => ({
banners: state.banners,
recommends: state.recommends
});

const mapDispatchToProps = dispatch => ({
fetchData: () => dispatch(fetchBannersAction())
});

export default connect(mapStateToProps, mapDispatchToProps)(Detail);

1.5. redux-devtool

配置 store/index.js

1
2
3
4
5
6
7
8
9
10
11
12
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer.js";

const middlewareEnhancer = applyMiddleware(thunk);

const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true }) || compose;

const store = createStore(reducer, composeEnhancers(middlewareEnhancer));

export default store;

1.6. reducer 模块拆分

counter 模块: store/counter/index.js

1
2
3
import reducer from "./reducer.js";

export default reducer;

remoteData 模块: store/remote-data/index.js

1
2
3
import reducer from "./reducer.js";

export default reducer;

出口文件: store/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { createStore, applyMiddleware, compose, combineReducers } from "redux";
import thunk from "redux-thunk";
import counterReducer from "./counter";
import remoteDataReducer from "./remote-data";

//会把counter, remoteData 分别作为一个模块, 获取state的数据时,需要先找到对应的模块,再在该模块中找到state中的数据
const reducer = combineReducers({
counter: counterReducer,
remoteData: remoteDataReducer
});

const middlewareEnhancer = applyMiddleware(thunk);

const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true }) || compose;

const store = createStore(reducer, composeEnhancers(middlewareEnhancer));

export default store;

state 的使用: views/Home.jsx

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
import React, { PureComponent } from 'react'
import { connect } from "react-redux"
import {
addCounterAction,
subCounterAction
} from '../store/counter/actionCreators'
import store from "../store"

export class Home extends PureComponent {
constructor(){
super()
this.state = {
count: store.getState().counter.count // 此处也是需要找到对应模块的数据
}
}

addCounter(num) {
const { addCounter } = this.props
addCounter(num)
}

subCounter(num) {
const { subCounter } = this.props
subCounter(num)
}

render() {
const { count } = this.props
return (
<div>
<h1>Home</h1>
<h2>当前计数: {count}</h2>
<button onClick={e => this.addCounter(2)}>+2</button>
<button onClick={e => this.subCounter(1)}>-1</button>
</div>
)
}
}

// state在使用时,需要先找到对应的模块,再去找该模块中的数据
const mapStateToProps = (state) => ({count: state.counter.count})

// dispatch(action) 正常使用,没有区别
const mapDispatchToProps = (dispatch) => ({
addCounter: (num) => dispatch(addCounterAction(num))
subCounter: (num) => dispatch(subCounterAction(num))
})

export default connect(mapStateToProps, mapDispatchToProps)(Home)

二. redux 进阶

2.1. redux-toolkit

2.1.1. redux-toolkit 基本使用

1.安装 redux-toolkit

1
npm install @reduxjs/toolkit react-redux

2.核心 API

  • configureStore:包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合 slice reducer 添加提供的任何 Redux 中间件,redux-thunk 默认包含,并启用 Redux DevTools Extension
  • createSlice:接受 reducer 函数的对象、切片名称和初始状态值,并自动生成切片 reducer,并带有相应的 actions;createSlice 返回值是一个对象,包含所有的 actions;具有以下几个重要的参数:
    • name:用户标记 slice 的名词
      • 在之后的 redux-devtool 中会显示对应的名词
    • initialState:初始化值
      • 第一次初始化时的值
    • reducers:相当于之前的 reducer 函数
      • 对象类型,并且可以添加很多的函数
      • 函数类似于 redux 原来 reducer 中的一个 case 语句
      • 函数的参数
        • 参数一:state
        • 参数二:调用这个 action 时,传递的 action 参数
  • createAsyncThunk: 接受一个动作类型字符串和一个返回承诺的函数,并生成一个 pending/fulfilled/rejected 基于该承诺分派动作类型的 thunk

3.使用过程

store/counter.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
name: "counter",
initialState: {
count: 66
},
reducers: {
addCounter(state, action) {
console.log("add", state, action);
state.count += action.payload;
},
subCounter(state, action) {
console.log("sub", state, action);
state.count -= action.payload;
}
}
});

export default counterSlice.reducer;

export const { addCounter, subCounter } = counterSlice.actions;

store/index.js

1
2
3
4
5
6
7
8
9
10
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counter";

const store = configureStore({
reducer: {
counter: counterReducer
}
});

export default store;

views/Home.jsx

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
import React, { PureComponent } from "react";
import { connect } from "react-redux";
import store from "../store";
import { addCounter, subCounter } from "../store/counter.js";

export class Home extends PureComponent {
addCounter(num) {
const { addCounter } = this.props;
addCounter(num);
}

subCounter(num) {
const { subCounter } = this.props;
subCounter(num);
}

render() {
const { count } = this.props;
return (
<div>
<h1>Home</h1>
<h2>当前计数: {count}</h2>
<button onClick={() => this.addCounter(1)}>+1</button>
<button onClick={() => this.subCounter(1)}>-1</button>
</div>
);
}
}

const mapStateToProps = state => ({ count: state.counter.count });

const mapDispatchToProps = dispatch => ({
addCount: num => dispatch(addCount(num)),
subCount: num => dispatch(subCount(num))
});

export default connect(mapStateToProps, mapDispatchToProps)(Home);

2.1.2. redux-toolkit 异步操作

方式一: 在组件中请求数据

views/Detail.jsx

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
import React, { PureComponent } from 'react'
import { connect } from "react-redux"
import axios from "axios"
import {
fetchBanners,
fetchRecommends
} from '../store/remoteData.js'

export class Detail extends PureComponent {
componentDidMount() {
const { fetchBanners, fetchRecommends} = this.props

axios.get("url").then(res => {
const banners = res.data.data.banner.list
const recommends = res.data.recommend.list

fetchBanners(banners)
fetchRecommends(recommends)
}).catch(err => {
console.log(err)
})
}

render() {
const { banners, recommends } = this.props
return (
<div>
{/** ...对banners和recommends的展示 */}
</div>
)
}
}

const mapStateToProps = (state) => ({
banners: state.banners,
recommends: state.recommends
})

const mapDispatchToProps = (dispatch) => ({
fetchBanners: (data) => dispatch(fetchBanners(data))
fetchRecommends: (data) => dispatch(fetchRecommends(data))
})

export default connect(mapStateToProps, mapDispatchToProps)(Detail)

方式二: 在 createAsyncThunk 请求,使用 extraReducers 的对象类型处理

store/remoteData.js

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
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"
import axios from "axios"

const fetchDataAction = createAsyncThunk("fetchData", async () => {
const res = await axios.get("url")
return res.data.data
})

const remoteDataSlice = createSlice({
name: "remoteData",
initialState: {
banners: [],
recommends: []
},
reducers: {
fetchBanners(state, { payload }) {
state.banners = payload
},
fetchRecommends(state, { payload }) {
state.recommends = payload
}
},
extraReducers: {
[fetchDataAction.pending]: (state, action) => {
const { type, payload } = action
console.log("pending", type, payload)
},
[fetchDataAction.fulfilled]: (state, { payload }) => {
state.banners = payload.banner.list
state.recommends = payload.recommend.list
},
[fetchDataAction.rejected]: (state, action) => {
console.log("rejected")
}
}
})

export default remoteDataSlice.reducer

export const { fetchBanners, fetchRecommends } = remoteDataSlice.actions
export fetchDataAction

方式三: 在 createAsyncThunk 请求,使用 extraReducers 的函数类型处理

store/remoteData.js

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
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"
import axios from "axios"

const fetchDataAction = createAsyncThunk("fetchData", async () => {
const res = await axios.get("url")
return res.data.data
})

const remoteDataSlice = createSlice({
...,
extraReducers: builder => {
builder
.addCase(fetchDataAction.pending, state => {
console.log("pending")
})
.addCase(fetchDataAction.fulfilled, (state, { payload }) => {
state.banners = payload.banner.list
state.recommends = payload.recommend.list
})
.addCase(fetchDataAction.rejected, () => {
console.log("rejected")
})
}
})

export default remoteDataSlice.reducer

export const { fetchBanners, fetchRecommends } = remoteDataSlice.actions
export fetchDataAction

方式四: 在 createAsyncThunk 请求,使用回调函数的参数 dispatch 派发 action

store/remoteData.js

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
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"
import axios from "axios"

const fetchDataAction = createAsyncThunk(
"fetchData",
async (payload, { dispatch, getState }) => {
const res = await axios.get("url")
const banners = res.data.data.banner.list
const recommends = res.data.data.recommend.list
dispatch(fetchBanners(banners))
dispatch(fetchRecommends(recommends))
})

const remoteDataSlice = createSlice({
name: "remoteData",
initialState: {
banners: [],
recommends: []
},
reducers: {
fetchBanners(state, { payload }) {
state.banners = payload
},
fetchRecommends(state, { payload }) {
state.recommends = payload
}
}
})

export default remoteDataSlice.reducer

export const { fetchBanners, fetchRecommends } = remoteDataSlice.actions
export fetchDataAction

2.2. 原理实现

2.2.1. combineReducers

1
2
3
4
5
6
7
8
9
// combineReducers实现原理
function reducer(state = {}, action) {
// 返回一个对象, store的state
return {
counter: counterReducer(state.counter, action),
home: homeReducer(state.home, action),
user: userReducer(state.user, action)
};
}

2.2.2. react-redux

context

src/hoc/store_context.js

1
2
3
import { createContext } from "react";

export const StoreContext = createContext();

provider

src/App.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React, { PureComponent } from "react";
import store from "./store";
import Home from "./views/Home.jsx";
import { StoreContext } from "./hoc/store_context.js";

export class APP extends PureComponent {
render() {
return (
<StoreContext.Provider value={store}>
<Home />
</StoreContext.Provider>
);
}
}

export default App;

connect

src/hoc/connect.js

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
import { PureComponent } from "react";
import { StoreContext } from "./store_context.js";

export default function connect(mapStateToProps, mapDispatchToProps) {
return WrapperComponent => {
class ConnectCom extends PureComponent {
constructor(props, context) {
super(props);
console.log(context);
this.state = mapStateToProps(context.getState());
}

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

componentWillUnmount() {
this.unsubscribe();
}

render() {
const stateProps = mapStateToProps(this.context.getState());
const dispatchProps = mapDispatchToProps(this.context.dispatch);

return (
<WrapperComponent
{...this.props}
{...stateProps}
{...dispatchProps}
/>
);
}
}

ConnectCom.contextType = StoreContext;

return ConnectCom;
};
}

2.2.3. middleware

2.3.3.1. printLog
1
2
3
4
5
6
7
8
9
10
11
12
13
function printLog(store) {
const next = store.dispatch;

function logAndDispatch(action) {
console.log("当前派发的action:", action);
// 真正派发的代码: 使用之前的dispatch进行派发
next(action);
console.log("派发之后的结果:", store.getState());
}

// monkey patch: 猴补丁 => 篡改现有的代码, 对整体的执行逻辑进行修改
store.dispatch = logAndDispatch;
}
2.3.3.2. thunk
1
2
3
4
5
6
7
8
9
10
11
function thunk(store) {
const next = store.dispatch;
function dispatchThunk(action) {
if (typeof action === "function") {
action(store.dispatch, store.getState);
} else {
next(action);
}
}
store.dispatch = dispatchThunk;
}
2.3.3.3. applyMiddleware
1
2
3
4
5
function applyMiddleware(store, ...middlewares) {
middlewares.forEach(middleware => {
middleware(store);
});
}