淺談redux以及react-redux簡單實現(xiàn)
寫在前頭
redux 簡介
隨著 JavaScript 單頁應(yīng)用開發(fā)日趨復(fù)雜,JavaScript 需要管理比任何時候都要多的 state (狀態(tài))。 這些 state 可能包括服務(wù)器響應(yīng)、緩存數(shù)據(jù)、本地生成尚未持久化到服務(wù)器的數(shù)據(jù),也包括 UI 狀態(tài),如激活的路由,被選中的標簽,是否顯示加載動效或者分頁器等等。
管理不斷變化的 state 非常困難。如果一個 model 的變化會引起另一個 model 變化,那么當 view 變化時,就可能引起對應(yīng) model 以及另一個 model 的變化,依次地,可能會引起另一個 view 的變化。直至你搞不清楚到底發(fā)生了什么。state 在什么時候,由于什么原因,如何變化已然不受控制。 當系統(tǒng)變得錯綜復(fù)雜的時候,想重現(xiàn)問題或者添加新功能就會變得舉步維艱。
如果這還不夠糟糕,考慮一些來自前端開發(fā)領(lǐng)域的新需求,如更新調(diào)優(yōu)、服務(wù)端渲染、路由跳轉(zhuǎn)前請求數(shù)據(jù)等等。前端開發(fā)者正在經(jīng)受前所未有的復(fù)雜性,難道就這么放棄了嗎?當然不是。
這里的復(fù)雜性很大程度上來自于:我們總是將兩個難以理清的概念混淆在一起:變化和異步。 如果把二者分開,能做的很好,但混到一起,就變得一團糟。一些庫如 React 試圖在視圖層禁止異步和直接操作 DOM 來解決這個問題。美中不足的是,React 依舊把處理 state 中數(shù)據(jù)的問題留給了我們自己。而 redux 就可以來幫我管理這些狀態(tài);
demo 演示
](http://img.jbzj.com/file_images/article/201808/2018082810172329.gif)
demo 結(jié)構(gòu)樹
├── config-overrides.js ├── .gitignore ├── package.json ├── package-lock.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── README.md └── src ├── App.js ├── Demo │ ├── actionCreate.js │ ├── Demo.jsx │ ├── react-redux.js │ ├── reducer.js │ ├── redux.js │ ├── style.css │ └── thunk.js └── index.js
一、 redux API createStore 的實現(xiàn)
首先我們先結(jié)合 reducer 以及 action 的知識簡單實現(xiàn)開頭展示的 demo, 并逐步揭曉 createStore 的神秘面紗;
1.1 準備工作:
創(chuàng)建 reducer 并導(dǎo)出 reducer
// reducer.js
const initState = { user: 'qianyin', age: 18, sex: '男' };
export const reducer = (state=initState, action) => {
switch(action.type){
case 'USER_UPDATE':
return {...state, ...action.payload};
case 'AGE_GROW':
return {...state, age: state.age + 1};
case 'SEX_UPDATE':
return {...state, ...action.payload};
default:
return state;
}
}
創(chuàng)建 action 創(chuàng)建函數(shù)
// actionCreate.js
export const changeUser = (user) => {
return {
payload:{user},
type: 'USER_UPDATE',
};
}
export const changeAge = () => {
return { type: 'AGE_GROW' };
}
通過 react 在頁面上預(yù)先繪制出基本的元素
/* style.css */
.btn{
height: 31px;
}
.input{
height: 25px;
}
// Demo.jsx
import React from 'react';
import './style.css';
export default class Demo extends React.Component{
onChange = () => {}
onClick = () => {}
render(){
return (
<div>
<p>user: xxx, age: xxx</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}>年齡增長</button>
</div>
);
}
}
最終頁面將渲染如下:

1.2 demo 的初次實現(xiàn)代碼
- 創(chuàng)建全局狀態(tài) state;
- 創(chuàng)建監(jiān)聽隊列;
- 針對監(jiān)聽隊列,新增函數(shù)用于將指定監(jiān)聽對象添加到隊列中;
- 在函數(shù) dispatch 中執(zhí)行 reducer 將返回值作為新的 state, 同時依次執(zhí)行監(jiān)聽對象;
- 默認執(zhí)行一次 dispatch 給定一個 type 相對唯一的 action, 目的是為了匹配 reducer 的默認狀態(tài)值,從而實現(xiàn)對 redux state 的初始化;
- 在組件 Demo 通過在函數(shù) update 使用 this.setState 將全局 state 保存到 react state 中,并將函數(shù) update 添加到監(jiān)聽隊列中;從而使得當我們一旦試圖通過 dispatch 修改全局狀態(tài)時,能夠及時更新 react state 最終觸發(fā) react 生命周期 render;
- 在 react 生命周期 componentDidMount 中我們除了將 update 添加到監(jiān)聽隊列以外,還需手動執(zhí)行一次 update 其主要目的就是為了首次初始化 react state;
// Demo.jsx
import React from 'react';
import { changeAge, changeUser } from './actionCreate';
import { reducer } from './reducer';
import './style.css';
let state;
const listeners = [];
const subscribe = (listener) => {
listeners.push(listener);
}
const dispatch = (action) => {
state = reducer(state, action);
console.log(state);
listeners.forEach(v => v());
}
dispatch({type: '%$&HJKAJJHDJHJ'});
export default class Demo extends React.Component{
state = {user: 'xxx', age: 'xxx'};
componentDidMount(){
subscribe(this.update);
this.update();
}
update = () => {
this.setState(state);
}
onChange = (e) => {
dispatch(changeUser(e.target.value));
}
onClick = () => {
dispatch(changeAge());
}
render(){
return (
<div>
<p>user: {this.state.user}, age: {this.state.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}>年齡增長</button>
</div>
);
}
}
1.3 API createStore 的實現(xiàn)
其實上文的代碼中對于 createStore 的實現(xiàn)原理已經(jīng)基本描述清除,下面我們只是單純的對代碼進行了簡單的封裝;當然為了能夠獲取到 state 我們專門增加了一個函數(shù) getState 來實現(xiàn)它;
createStore 函數(shù)實現(xiàn)
// redux.js
export const createStore = (reducer) => {
// 聲明常量
let state;
const listeners = [];
// 獲取狀態(tài)
const getState = () => {
return state;
}
// 添加監(jiān)聽對象
const subscribe = (listener) => {
listeners.push(listener);
}
// [1]執(zhí)行reducer修改狀態(tài) [2]遍歷執(zhí)行監(jiān)聽對象
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(v => v());
}
// 初始化 state
dispatch({type: '%$&HJKAJJHDJHJ'});
// 暴露接口
return {getState, subscribe, dispatch};
}
調(diào)用 createStore 并對 demo 進行修改
// Demo.jsx
import React from 'react';
import './style.css';
import { changeAge, changeUser } from './actionCreate';
import { reducer } from './reducer';
import { createStore } from './redux';
const store = createStore(reducer);
export default class Demo extends React.Component{
state = {user: 'xxx', age: 'xxx'};
componentDidMount(){
store.subscribe(this.update);
this.update();
}
update = () => {
this.setState(store.getState());
}
onChange = (e) => {
store.dispatch(changeUser(e.target.value));
}
onClick = () => {
store.dispatch(changeAge());
}
render(){
return (
<div>
<p>user: {this.state.user}, age: {this.state.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}>年齡增長</button>
</div>
);
}
}
二、 react-redux API Provider 的實現(xiàn)
在 react 中大多數(shù)情況下我們需要將狀態(tài)傳遞給后代組件進行使用的,當然通過 props 是可以實現(xiàn)狀態(tài)從父級到子級的傳遞,但是當狀態(tài)需要傳遞的層級比較深的情況下再使用 props 就顯得無力了,那么在 react-redux 中它是如何實現(xiàn)對 store 的傳遞的呢?
2.1 react context 的引入
在 App.js 中創(chuàng)建 store 并通過 context 傳遞 store
// App.js
import React, { Component } from 'react';
import propTypes from 'prop-types';
import { createStore } from './Demo/redux';
import { reducer } from './Demo/reducer';
import Demo from './Demo/Demo';
// 創(chuàng)建 store
const store = createStore(reducer);
class App extends Component {
// 聲明 childContextTypes 狀態(tài)屬性類型
static childContextTypes = {
store: propTypes.object
};
// 設(shè)置 childContext
getChildContext(){
return {store}
}
render() {
return <Demo />;
}
}
export default App;
在子組件 Demo 中通過 context 獲取 store 并對代碼進行簡單修改
// Demo.jsx
import React from 'react';
import propTypes from 'prop-types';
import './style.css';
import { changeAge, changeUser } from './actionCreate';
export default class Demo extends React.Component{
// 設(shè)置 context 狀態(tài)值類型
static contextTypes = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
// 獲取store
this.store = context.store;
this.state = {user: 'xxx', age: 'xxx'};
}
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
this.setState(this.store.getState());
}
onChange = (e) => {
this.store.dispatch(changeUser(e.target.value));
}
onClick = () => {
this.store.dispatch(changeAge());
}
render(){
return (
<div>
<p>user: {this.state.user}, age: {this.state.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}>年齡增長</button>
</div>
);
}
}
2.2 封裝代碼實現(xiàn) Provider
通過 react context 我們實現(xiàn)了對 store 的傳遞,到這里 Provider 的功能以及實現(xiàn)原理基本上應(yīng)該算是清晰了,無非就是對組件進行包裹同時通過 react context 來傳遞共享 store;那么接下來我們通過對代碼的封裝來實現(xiàn) Provider 組件;
Provider 組件:實現(xiàn)對 store 的傳遞
// react-redux.js
import React from 'react';
import propTypes from 'prop-types';
export class Provider extends React.Component{
// 設(shè)置 childContext 狀態(tài)值類型
static childContextTypes = {
store: propTypes.object
};
// 設(shè)置 childContext
getChildContext(){
return {store: this.props.store}
}
render(){
return this.props.children;
}
}
重寫 App.js: 對 Provider 組件的調(diào)用
// App.js
import React, { Component } from 'react';
import { createStore } from './Demo/redux';
import { Provider } from './Demo/react-redux';
import { reducer } from './Demo/reducer';
import Demo from './Demo/Demo';
// 創(chuàng)建 store
const store = createStore(reducer);
class App extends Component {
render() {
// 調(diào)用接口 Provider
return <Provider store={store}><Demo /></Provider>;
}
}
export default App;
三、 react-redux API connect 高階組件的實現(xiàn)
上文中在后代組件如果需要獲取 store 則需要手動通過獲取 react context 來調(diào)用 store 并且需要顯性的調(diào)用 store 內(nèi)部的方法來進行一些操作;接下來我們來實現(xiàn)這么一個高階組件 connect,我們只需要提供所需的 redux state 以及 action 創(chuàng)建函數(shù),即可通過 props 獲取到相應(yīng)的 redux state , 并且允許直接通過 props 調(diào)用action 創(chuàng)建函數(shù)來試圖修改 redux state ;
創(chuàng)建高階組件 connect
// react-redux.js
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
render(){
return <Component />
}
}
}
獲取store
// react-redux.js
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
// 設(shè)置 context 狀態(tài)值類型
static contextType = {
store: propTypes.object
};
// [1]獲取 store [2]設(shè)置空 react state
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
render(){
return <Component />
}
}
}
添加監(jiān)聽對象,并嘗試通過 props 將狀態(tài)傳遞給子組件
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
static contextType = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
// [1]添加監(jiān)聽對象 [2]手動執(zhí)行監(jiān)聽對象,初始化 react state
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
// 獲取全部redux state 并添加到 react state
const state = this.store.getState();
this.setState(state);
}
render(){
// 通過 props 將 react state 全部傳給子組件
return <Component {...this.state} />
}
}
}
通過 mapStateToProps 獲取指定 redux state
// react-redux.js
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
static contextType = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
// 執(zhí)行 mapStateToProps 只獲取用戶指定需求的 state
const state = this.store.getState();
const filterState = mapStateToProps(state);
this.setState(filterState);
}
render(){
return <Component {...this.state} />
}
}
}
通過 mapDispatchToProps 獲取 action 創(chuàng)建函數(shù): 使用 dispatch 包裹后返回
// react-redux.js
// react-redux.js
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
static contextTypes = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
// 處理 state ===> 獲取用戶指定的 state
const state = this.store.getState();
const filterState = mapStateToProps(state);
// 使用 dispatch 對 mapDispatchToProps 中的 action 創(chuàng)建函數(shù)進行包裹后返回
const actionFun = {};
for(let key in mapDispatchToProps){
actionFun[key] = (...args) => {
this.store.dispatch(mapDispatchToProps[key](...args));
}
}
// 一種簡寫方式: 騷操作
// const actionFun = Object.keys(mapDispatchToProps)
// .reduce((total, item) => {
// return { ...total, [item]: (...args) => {dispatch(mapDispatchToProps[item](...args));}
// } } ,{});
this.setState({...filterState, ...actionFun});
}
render(){
return <Component {...this.state} />
}
}
}
調(diào)用高階組件:修改 Demo.jsx
// Demo.jsx
import React from 'react';
import { changeAge, changeUser } from './actionCreate';
import { connect } from './react-redux';
import './style.css';
// 編寫 mapStateToProps 參數(shù) redux state 返回所需的 redux state
const mapStateToProps = (state) => {
return {user: state.user, age: state.age};
}
// 調(diào)用高階組件
@connect(mapStateToProps, {changeAge, changeUser})
export default class Demo extends React.Component{
onChange = (e) => {
this.props.changeUser(e.target.value);
}
onClick = () => {
this.props.changeAge();
}
render(){
return (
<div>
<p>user: {this.props.user}, age: {this.props.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}>年齡增長</button>
</div>
);
}
}
四、redux API bindactioncreators 的實現(xiàn)
在上文我們對 mapDispatchToProps 的處理過程就是 API bindactioncreators 的功能: 將給定 action 創(chuàng)建函數(shù)使用 dispatch 進行包裹后返回;
封裝 bindactioncreators
// redux.js
export const bindactioncreators = (mapDispatchToProps, dispatch) => {
const actionFun = {};
// 遍歷 mapDispatchToProps 中每個 action 創(chuàng)建函數(shù) 并使用 dispatch 包裹后返回
for(let key in mapDispatchToProps){
actionFun[key] = (...args) => {
dispatch(mapDispatchToProps[key](...args));
}
}
return actionFun;
// 一種簡寫方式: 騷操作
// return actionFun = Object.keys(mapDispatchToProps)
// .reduce((total, item) => {
// return { ...total, [item]: (...args) => {dispatch(mapDispatchToProps[item](...args));}
// } } ,{});
}
修改 connect :
// react-redux.js
import { bindactioncreators } from './redux';
....
export const connect = (mapStateToProps, mapDispatchToProps) => (Component) => {
return class NewComponent extends React.Component{
static contextTypes = {
store: propTypes.object
};
constructor(props, context){
super(props, context);
this.store = context.store;
this.state = {};
}
componentDidMount(){
this.store.subscribe(this.update);
this.update();
}
update = () => {
const state = this.store.getState();
const filterState = mapStateToProps(state);
// 調(diào)用 API bindactioncreators
// 對 mapDispatchToProps 內(nèi)每個 action 創(chuàng)建函數(shù)使用 dispatch 進行包裹后返回
const actionFun = bindactioncreators(mapDispatchToProps, this.store.dispatch);
this.setState({...filterState, ...actionFun});
}
render(){
return <Component {...this.state} />
}
}
}
五、redux API applyMiddleware 的實現(xiàn)
到此簡化版的 react-redux 算是已經(jīng)初步完成,但是假如我們想要我們的 age 值的增長是一個異步操作,比如:通過按鈕點擊后經(jīng)過兩秒再修改 age ,而不是一點擊按鈕就立即修改值;這樣我們又該怎么實現(xiàn)呢?
當然我們可以通過 setTimeout 兩秒后再執(zhí)行 action 創(chuàng)建函數(shù),比如這樣:
onClick = () => {
setTimeout(()=>{
// 兩秒后執(zhí)行 action 創(chuàng)建函數(shù)
this.props.changeAge();
}, 2000);
}
但是呢事實上我們并不愿意像上面那么整,我們想要這么一種效果:我們只需要簡單的調(diào)用 action 創(chuàng)建函數(shù)即可實現(xiàn)異步操作,而不是需要進行額外的操作;這時我們就需要為我們的 react-redux 編寫一個中間件來實現(xiàn)這么一個效果;
5.1 準備工作
新增action 創(chuàng)建函數(shù)
在這之前我們所有的 acton 創(chuàng)建函數(shù)都是直接返回一個 action 對象,下面我們寫一個不一樣的 action 創(chuàng)建函數(shù), 它返回的不再是一個 action 對象而是一個函數(shù),并且該函數(shù)接收兩個參數(shù) dispatch 以及 getState, 在該函數(shù)內(nèi)部我們進行相應(yīng)的異步操作,比如:修改 age 值;
// actionCreate.js
export const asyncChangeAge = () => {
// 返回函數(shù)
return (dispatch, getState) => {
setTimeout(v=>{
console.log('==>', getState());
dispatch({type: 'AGE_GROW'});
}, 1000);
}
}
修改頁面:新增按鈕并綁定點擊事件,并且調(diào)用 asyncChangeAge 函數(shù);
// Demo.jsx
// Demo.jsx
import React from 'react';
import './style.css';
// 導(dǎo)入 asyncChangeAge
import { changeAge, changeUser, asyncChangeAge } from './actionCreate';
import { connect } from './react-redux';
const mapStateToProps = (state) => {
return {user: state.user, age: state.age};
}
// 添加 asyncChangeAge
@connect(mapStateToProps, {changeAge, changeUser, asyncChangeAge})
export default class Demo extends React.Component{
onChange = (e) => {
this.props.changeUser(e.target.value);
}
onClick = () => {
this.props.changeAge();
}
// 點擊事件
onClickAsync = () => {
this.props.asyncChangeAge();
}
render(){
return (
<div>
<p>user: {this.props.user}, age: {this.props.age}</p>
user:
<input type="text" className="input" onChange={this.onChange}/>
<button className="btn" onClick={this.onClick}>年齡增長</button>
{/* 新增按鈕 */}
<button className="btn" onClick={this.onClickAsync}>
異步增長
</button>
</div>
);
}
}
頁面的變化其實很簡單就是新增了一個按鈕:

5.2 需求實現(xiàn)
接下來我們先什么都不考慮先來實現(xiàn)我們的需求,現(xiàn)在 action 創(chuàng)建函數(shù) asyncChangeAge 因為返回的是一個對象,其 type 值為 undefined 所以當我們點擊按鈕時 reducer 將會一直匹配到默認情況,返回的將是當前的狀態(tài),接下來我們先讓我們的 action 創(chuàng)建函數(shù) asyncChangeAge 生效,達到異步修改狀態(tài)的作用;
擴展 createStore
既然 asyncChangeAge 返回的不再是一個 action 對象,而是一個函數(shù);那么其實我們要做的事情是很簡單的,我們只需要針對 createStore 中的返回值 dispatch 進行一個簡單的擴展即可;通過判斷 dispatch 中的 action 參數(shù)是否是函數(shù)而進行不同的操作:
// redux.js
export const createStore = (reducer) => {
......
// 在createStore 我們對返回值 dispatch 進行了封裝
const dispatchExtend = (action) => {
if(typeof action === 'function'){
// action 為函數(shù),執(zhí)行函數(shù)
action(dispatch, getState);
} else {
// action 為非函數(shù)(對象)調(diào)用dispatch
dispatch(action);
}
}
return {getState, dispatch: dispatchExtend, subscribe};
}
5.3 抽離封裝
上文我們通過對 createStore 的返回值 dispatch 進行了擴展,實現(xiàn)了 redux-react 的異步操作,但問題是我們將代碼寫死在 createStore 中了,redux-react 的異步操作應(yīng)該是一個可選項而不應(yīng)該是必選項;
重新擴展 createStore :
新增參數(shù) middleware (函數(shù)), 在函數(shù) createStore 開始位置判斷 middleware 是否存在,存在則執(zhí)行;
// redux.js
export const createStore = (reducer, middleware) => {
// 判斷 middleware 是否存在,存在則執(zhí)行
if(middleware){
return middleware(createStore)(reducer);
}
let state;
const listeners = [];
const getState = () => {
return state;
}
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(v => v());
}
const subscribe = (listener) => {
listeners.push(listener);
}
dispatch({type: '%$&HJKAJJHDJHJ'});
return {getState, dispatch, subscribe};
}
編寫函數(shù) applyMiddleware ,在創(chuàng)建 store 時 作為 createStore 第二參數(shù)
// App.js
const applyMiddleware = (createStore) => (redux)=> {
// 在這里進行創(chuàng)建 store
const store = createStore(redux);
// 返回store
return {...store}
}
const store = createStore(reducer, applyMiddleware);
在 applyMiddleware 函數(shù)內(nèi)擴展 dispatch
上文 applyMiddleware 函數(shù)并其實沒做任何事情, 只是在 createStore 函數(shù)外面套了一層函數(shù),那么接下來我們做點正事,來擴展一下我們的 dispatch
// App.js
const applyMiddleware = (createStore) => (redux)=> {
const store = createStore(redux);
const midApi = {
getState: store.getState,
dispatch: (...args) => {dispatch(...args);}
};
const dispatch = (action) => {
if( typeof action === 'function' ){
action(midApi.dispatch, midApi.getState);
} else {
store.dispatch(action);
}
}
return {
...store,
dispatch
};
}
5.4 擴展分離
上文已經(jīng)實現(xiàn)了我們想要的效果了,我們在 applyMiddleware 對 dispatch 進行了擴展;然而我們是那么容易滿足的嘛,當然不是的??! applyMiddleware 中對 dispatch 的擴展我們還可以將其單獨提出來封裝成一個函數(shù);
重寫 applyMiddleware ,再給 applyMiddleware 包裹一層函數(shù): 將對 dispatch 的擴展抽離,封裝成方法;
// App.js
const applyMiddleware = (middleware) => (createStore) => (redux)=> {
const store = createStore(redux);
const midApi = {
getState: store.getState,
dispatch: (...args) => {dispatch(...args);}
};
const dispatch = middleware(midApi)(store.dispatch);
return {
...store,
dispatch
};
}
thunk 中間件: 其實 thunk 才是真正的中間件;applyMiddleware 只是用來綁定中間件的
// App.js
const thunk = ({dispatch, getState}) => next => (action) => {
if(typeof action === 'function'){
action(dispatch, getState);
} else {
next(action);
}
};
在調(diào)用 createStore 時綁定中間件 thunk
// App.jsx const store = createStore(reducer, applyMiddleware(thunk));
5.5 代碼整理
這一步只是將 applyMiddleware 函數(shù)移到 redux.js 文件中;同時將 thunk 函數(shù)寫到文件 thunk.jsx 中,然后在 App.js 中引用 applyMiddleware 以及 thunk 而已;
看下最終效果(效果和上一個錄屏是一樣樣的)

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
解決React報錯React?Hook?useEffect?has?a?missing?dependency
這篇文章主要為大家介紹了解決React報錯React?Hook?useEffect?has?a?missing?dependency,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
React如何使用localStorage及實現(xiàn)刪除筆記操作過程
這篇文章主要介紹了React如何使用localStorage及實現(xiàn)刪除筆記操作過程,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友一起看看吧2024-12-12
解決React報錯The?tag?is?unrecognized?in?this?browser
這篇文章主要為大家介紹了解決React報錯The?tag?is?unrecognized?in?this?browser示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
React 模塊聯(lián)邦多模塊項目實戰(zhàn)詳解
這篇文章主要介紹了React 模塊聯(lián)邦多模塊項目實戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10

