一. 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 7 8 9
| 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 5 6 7
| 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 45 46 47
| 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
| 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 43 44
| 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
| 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
| 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 } } 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 ExtensioncreateSlice
:接受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 38 39 40 41
| 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)
|
方式一: 在组件中请求数据
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
| 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 6
| function applyMiddleware(store, ...middlewares) { middlewares.forEach(middleware => { middleware(store) }) }
|