一. 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 安装与配置
安装
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 流程解析


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
安装
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 函数
异步处理流程

1.4.3.2. redux-thunk
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);
|
配置 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";
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> ) } }
const mapStateToProps = (state) => ({count: state.counter.count})
const mapDispatchToProps = (dispatch) => ({ addCounter: (num) => dispatch(addCounterAction(num)) subCounter: (num) => dispatch(subCounterAction(num)) })
export default connect(mapStateToProps, mapDispatchToProps)(Home)
|
二. redux 进阶
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 as addCounterAction, subCounter as subCounterAction } 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 => ({ addCounter: num => dispatch(addCounterAction(num)), subCounter: num => dispatch(subCounterAction(num)) });
export default connect(mapStateToProps, mapDispatchToProps)(Home);
|
方式一: 在组件中请求数据
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
| function reducer(state = {}, action) { 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); next(action); console.log("派发之后的结果:", store.getState()); }
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); }); }
|