React組件化(以Ant-Design為例)
組件化編程,只需要去安裝好對應的組件,然后通過各式各樣的組件引入,實現快速開發
我們這里學習的是 Ant-design
(應該是這樣),它有很多的組件供我們使用
引入各種組件,可以方便開發,省著自己去二次封裝組件,同時也更好看了
快速起步
安裝antd組件庫
npm install antd
隨便一個組件里面引入
import { Button } from "antd";class App extends React.Component {render() {<div><Flex gap="small" wrap="wrap"><Button type="primary">Primary Button</Button><Button>Default Button</Button><Button type="dashed">Dashed Button</Button><Button type="text">Text Button</Button><Button type="link">Link Button</Button></Flex></div>}
}
export default App;
運行一下
大概就是這么個東西,一定學會看官方文檔,尤其這種組件類的,不看文檔就啥也做不了
AntDesign官方文檔
其他UI,但是是國外的
material-ui
大概就是這些東西,還有自定義主題的一些方法,因為每個版本的官方修改方式都不一樣,所以建議現用現查
Redux
非必須學習的項目,但是如果項目里用了就得學
Redux簡介
1.redux是一個專門用于做狀態管理的JS庫(不是react插件庫,只是名字像)。
2.它可以用在react, angular, vue等項目中, 但基本與react配合使用。
3.作用: 集中式管理react應用中多個組件共享的狀態。
也類似于VueX
什么時候需要用Redux
首先,Redux是在有很多很多組件的情況下,才有可能需要用到Redux的,如果是單個組件,就自然沒有很復雜的傳值需求了
Redux
適用于多交互、多數據源的場景。簡單理解就是復雜
從組件角度去考慮的話,當我們有以下的應用場景時,我們可以嘗試采用 Redux
來實現
- 某個組件的狀態需要共享時
- 一個組件需要改變其他組件的狀態時
- 一個組件需要改變全局的狀態時
除此之外,還有很多情況都需要使用 Redux 來實現(還沒有學 hook,或許還有更好的方法)
Redux工作流程
- store
store
是 Redux 的核心,可以理解為是 Redux 的數據流向指揮,我們可以將任何我們想要存放的數據放在 store
中,在我們需要使用這些數據時,我們可以從中取出相應的數據。因此我們需要先創建一個 store
,在 Redux 中可以使用 createStore
API 來創建一個 store
store是無法直接進行操作的,需要借助Redux進行操作
store是一個調度者,store是指揮者,不干活。需要任何操作,或者動作,都會去分發走,找到對應的擔當來做。(如果越過了store,action直接去找reducers,就有點類似去餐廳點餐,不找前臺點餐,直奔后廚要吃的😂)
- action
action
是 store
中唯一的數據來源,一般來說,我們會通過調用 store.dispatch
將 action 分發到 store
我們需要傳遞的 action
是一個對象,它必須要有一個 type
值(如果是初始化,就傳@@init@@
代表要初始化),data值在第一次傳參的時候可以為undefined,然后由Reducers來初始化賦值。
- reducers
在 Reducers 中,我們需要指定狀態的操作類型(type),要做怎樣的數據更新,因此這個類型是必要的。
因為Reducers中會有很多具體處理的Reducer,所以這里Reducers代表很多處理Reducer的集合。
reducer 會根據 action 的指示,對 state 進行對應的操作,然后返回操作后的 state
另外,Reducer可以加工狀態,加工的前提是有上一次的狀態值,如果沒有狀態值就要初始化一個狀態值。有了狀態值之后就可以進行下一步的加工。
手寫一個Redux精簡版
這里只是一個簡化,忽略了很多東西(比如Creators創建Action的過程),側重于展示store,reducer之間的關系
store.js文件:
創建一個store的js,導出供外界使用
import { createStore } from "redux";
//引入為store服務的Reducer
import countReducer from "./count_reducer";
//手動創建一個store
const store = createStore(countReducer);//全局只暴露這一個store對象
export default store;
count_reducer.js文件:
我們創建專門計數的reducer(簡單來說就是專門負責處理某件事的function),這里取名叫作count_reducer
所有的action通過dispatch
傳進去之后,類型,數據都托管于store這個中心,調用的組件只需要把 type,data
傳進去,剩下的只需要等著從store中獲取結果即可!
/*作為一個reducer應該有如下功能Store傳來(previousState,action),Reducer接收到參數做出判斷,previousState判斷是否是空,是空就得初始化。非空就按照action進行下一步操作action處理完之后,把處理好的newState(previousState處理之后的版本),返回給store,等待Store返回給React組件所以以上的這些操作,只能用function。所以Reducers里的Reducer本質就是函數動作完成后,將數據暫存給store,等待后續組件獲取值即可
*/export default function countReducer(previousState, action) {// 從Action對象里傳來的action對象,里面包含type和data,所以我們需要解構出來。等待后續處理const { type, data } = action;if (previousState === undefined) {// 如果之前的狀態是空的,就初始化之后還給store中心// 或者可以這么寫,給參數給默認值// function countReducer(previousState=0, action) {return 0;}// 判斷傳入功能類型switch (type) {case "add"://這里用原來的值+新傳入的值,得到新值return previousState + data * 1;case "sub"://這里用原來的值-新傳入的值,得到新值return previousState - data * 1;// 提一嘴,這里都是return,所以不用breakdefault:return previousState;}
}
Count.jsx文件:
調用方組件:
import React, { Component } from "react";
// 獲取store,用于獲取store中的狀態
import store from "../redux/store";export default class Count extends Component {// 之前準備的state,存儲值count 都不用存在了,因為已經托管給store了// 但是,自己組件內部的值其實是還需要放在state的,因為store只需要托管復雜共享的情況state = {// 這個實際上就不要了,因為保存在state中// countNumber: "",// 但是比如car這個屬性,只有自己用,就沒必要兜一圈放redux中了,放自己組件里就挺好car: "威朗Pro GS",};add = () => {const { value } = this.count;// store是分發中心,告訴reducer要干的事情,以及傳入的數值// 我們只需要將 type(要做的事情),value(原始值) 告訴store 。讓store去找對應的reducer操作即可// 對應的reducer拿到數值,做出判斷以及相應動作處理數據,處理好之后return值在store中等待組件get// 但注意,僅僅調用dispatch是不夠的,因為redux是第三方js。無法觸發頁面刷新// 所以需要檢測Redux里狀態改變時,就去調用render。這里用到了訂閱subscribe// 你都不用自己再解構接收值,所有的函數處理,值存儲,都在redux中做好了,只需要我們從store中get結果即可// 獲取store的地方,因為被監聽所以自動刷新了,觸發renderstore.dispatch({ type: "add", data: value });store.subscribe(() => {// 只要Redux里狀態改變時,就去調用render。這里用到了訂閱subscribe。手動觸發一下就行// 借助setState,傳個空值進去就可以觸發render重新渲染// 當然,我們也可以在index.js的根標簽監聽這個。監聽整個App組件,利用diffing算法全部更新,避免性能下降this.setState({});});};sub = () => {const { value } = this.count;store.dispatch({ type: "sub", data: value });store.subscribe(() => {// 手動監聽,觸發頁面重新渲染this.setState({});});};render() {console.log(store);return (<div>當前結果<h1>{store.getState()}</h1>選擇內容<select ref={(c) => (this.count = c)}><option value={1}>1</option><option value={2}>2</option><option value={3}>3</option></select><button onClick={this.add}>+</button><button onClick={this.sub}>-</button></div>);}
}
總結一下精簡案例
(1).去除Count組件自身的狀態
(2).src下建立:-redux-store.js-count_reducer.js(3).store.js:1).引入redux中的createStore函數,創建一個store2).createStore調用時要傳入一個為其服務的reducer3).記得暴露store對象(4).count_reducer.js:1).reducer的本質是一個函數,接收:preState,action,返回加工后的狀態2).reducer有兩個作用:初始化狀態,加工狀態3).reducer被第一次調用時,是store自動觸發的,傳遞的preState是undefined,傳遞的action是:{type:'@@REDUX/INIT_a.2.b.4}( _a.2.b.4是隨機數,為了避免和自己寫的type名有重合)(5).在index.js中監測store中狀態的改變,一旦發生改變重新渲染<App/>備注:redux只負責管理狀態,至于狀態的改變驅動著頁面的展示,要靠我們自己寫。
完整版Redux
上面的案例沒有寫action的創建,所以這里補齊就成為了完整版。
該文件專門為Count組件需要用的Reducer去生成action對象,封裝好type,這樣在調用組件傳一個data進來就可以了
新增文件:1.count_action.js 專門用于創建action對象2.constant.js 放置容易寫錯的type值
store.js文件:
創建一個store的js,導出供外界使用。這個和上面的沒區別,這里不贅述了
count_action.js文件:
/*該文件專門為Count需要用的Reducer去生成action對象,封裝好type,這樣在調用組件傳一個data進來就可以了
*/
import { ADD } from "./constant.js";// 注意,這里有個坑,就是我希望在這里返回對象的箭頭函數,最外側不能是花括號
// (data) => { type: ADD, data }; 如果是這樣,會把 type: ADD, data 這部分識別為函數體,就沒法返回對象了
// 所以我們用括號給括起來,就自動返回對象了
export const createAddAction = (data) => ({ type: ADD, data });
// 這兩種寫法等價
export function createIncrementAction(data) {// 要返回Action對象(返回type和data數據)return { type: ADD, data };
}
count_reducer.js文件:
本案例中,主要是引入常量來表示Type,避免了容易拼錯的問題
import { ADD, SUB } from "./constant.js";export default function countReducer(previousState, action) {// 從Action對象里傳來的action對象,里面包含type和data,所以我們需要解構出來。等待后續處理const { type, data } = action;if (previousState === undefined) {// 如果之前的狀態是空的,就初始化之后還給store中心// 或者可以這么寫,給參數給默認值// function countReducer(previousState=0, action) {return 0;}// 判斷傳入功能類型switch (type) {case ADD://這里用原來的值+新傳入的值,得到新值return previousState + data * 1;case SUB://這里用原來的值-新傳入的值,得到新值return previousState - data * 1;// 提一嘴,這里都是return,所以不用breakdefault:return previousState;}
}
Count.jsx文件:
調用方組件,這里主要強調變化的部分,省略了部分代碼:
import React, { Component } from "react";
// 傳入action函數,我們只需要傳入data即可
import { createAddAction } from "../redux/count_action";
// 獲取store,用于獲取store中的狀態
import store from "../redux/store";export default class Count extends Component {...省略add = () => {const { value } = this.count;// 引入了action后,就不需要我們手動定義type了,在action中已經做好定義了,我們只需要傳入data即可// store.dispatch({ type: "add", data: value });// 引入,并且直接用count_action所封裝好的函數來傳入參數store.dispatch(createAddAction(value * 1));store.subscribe(() => {// 監聽,store值變化重新renderthis.setState({});});};sub = () => {省略...};render() {return (省略...);}
}
異步action版
action有兩種類型,根據返回值類型來區分
- action的值為Object,則為同步action(store可以直接接收處理)
//返回一個對象
export const createSubAction = (data) => ({ type: SUB, data });
- action的值為function,則為異步action(只有function才能開啟異步任務,并且,store不可以直接接收處理異步action,需要通過中間件處理后才能接收)
export const createAddAsync = (data,time) => {return ()=>{setTimeout(() => {...省略}, time);}
};
之前的異步add方法,其本質還是在組件里寫的,可以看到,等待的異步過程沒有在Redux里面寫
addAsync = () => {const { value } = this.count;setTimeout(() => {store.dispatch(createAddAction(value * 1));}, 5000);
};
我們現在要把異步等待的操作,放在Redux的Action Creators里面,不放在組件里面等待了
調用方組件Count:
addAsync = () => {const { value } = this.count;//常規調用,看似沒問題store.dispatch(createAddAsync(value, 5000));store.subscribe(() => {// 手動監聽,觸發頁面重新渲染(也可以去監聽App組件)this.setState({});});
};
action組件:
export const createAddAsync = (data, time) => {return () => {// 這里其實只套了一個定時操作setTimeout(() => {// 通過store調用已經定義好的增加action,省著我們再寫了store.dispatch(createAddAction(data));}, time);};
};
但實際上這是有問題的,運行代碼會報錯
翻譯過來就是,store不直接接收action的值為function(異步action)
。想要接收必須去用一個中間件,讓store允許接收函數
引入中間件:npm install redux-thunk
需要我們安裝一下
引入完成之后,就需要在store.js
里面修改一下,用于支持異步action。做的這一切,只是為了讓store可以接收異步action返回的函數
store.js
:
//引入store創建,以及中間件
import { createStore, applyMiddleware } from "redux";
//引入為store服務的Reducer
import countReducer from "./count_reducer";
// 引入thunk給applyMiddleware中間件用
import thunk from "redux-thunk";
//手動創建一個store,傳入applyMiddleware(thunk)
const store = createStore(countReducer, applyMiddleware(thunk));//全局只暴露這一個store對象
export default store;
此時我們再看修改后的組件:
調用方組件Count(沒有變化):
addAsync = () => {const { value } = this.count;store.dispatch(createAddAsync(value, 5000));store.subscribe(() => {// 手動監聽,觸發頁面重新渲染(也可以去監聽App組件)this.setState({});});
};
action組件:
// 異步action,就是只action的值為函數,在異步action中,一般都會調用同步action,異步action不是必須要用的
export const createAddAsync = (data, time) => {return () => {// 這里其實只套了一個定時操作setTimeout(() => {// 調用已經定義好的增加actionstore.dispatch(createAddAction(data));console.log(data, time);}, time);};// 不用store調用dispatch也可以,因為dispatch會自動傳進來一個,這兩種完全等價// return (dispatch) => {// // 這里其實只套了一個定時操作// setTimeout(() => {// // 調用已經定義好的增加action// dispatch(createAddAction(data));// console.log(data, time);// }, time);// };
};
此時再測試,不再報錯。
配置完畢之后的store
如果我們給store傳入一個普通類型的Object action,store就會直接找Reducer去做處理
如果給store傳入一個異步類型的Function action,這個函數store就會幫你調用
總結下來,雖然異步的action調用的時候返回值是函數,但是最后一般都會調用同步action,來完成數據的操作
React18版本的store監聽刷新
之前給的store subscribe監聽刷新,是React17版本的,React18版本的可以參考這個
ReactDOM.render is no longer supported in React 18.
改造之后的index.js
// 引入React核心庫
import React from "react";
import { createRoot } from "react-dom/client";
// 引入App標簽
import App from "./App";
import store from "./redux/store";// React18版本監聽并刷新頁面
const root = createRoot(document.getElementById("root"));
root.render(<App />);//監測redux中狀態的改變,如redux的狀態發生了改變,那么重新渲染App組件
store.subscribe(() => {root.render(<App />);
});
總結異步action
(1).明確:延遲的動作不想交給組件自身,想交給action(2).何時需要異步action:想要對狀態進行操作,但是具體的數據靠異步任務返回。(3).具體編碼:1).npm install redux-thunk,并配置在store中2).創建action的函數不再返回一般對象,而是一個函數,該函數中寫異步任務。3).異步任務有結果后,分發一個同步的action去真正操作數據。(4).備注:異步action不是必須要寫的,完全可以自己等待異步任務的結果了再去分發同步action。
React-Redux基礎
安裝:npm install react-redux
安裝不上去就用這個:npm install react-redux --legacy-peer-deps
還不行就把package.json里面的這倆都刪了,在安裝React-Redux
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
引言
React-Redux是React專門出的Redux,屬于官方出品
宗旨在于將UI與redux分隔開,所有的操作都要經由容器組件
引言-簡化版連接UI組件與容器組件
前置知識
redux里的唯一的api:connect
;
使用的時候,一般都是這么用connect()();
簡單來說,得分開看connect()是一個返回一個函數的方法
connect()()是在connect()返回函數后,繼續再次調用這個返回的函數
就有點類似下面這個,調用connect()()
,最后會觸發a里的輸出OK
當然,a()也可以返回一個返回值
connect(){return a();
}a(){console.log("OK")
}
首先明確文件應該放在哪個包下面:
UI組件:components包下
容器組件:containers包下
且UI組件的里面,不能有任何關于Redux相關的API(store,actionCreator,dispatch… 這些API都不能引入了)。只能有UI組件以及UI組件動作相關的東西。在components里創建countTemplate.jsx
countTemplate.jsx
:
import React, { Component } from "react";export default class CountTemplate extends Component {// 純UI組件+頁面操作//加法increment = () => {// 這個是獲取頁面的選擇值const { value } = this.selectNumber;};...//異步加incrementAsync = () => {const { value } = this.selectNumber;};render() {//console.log('UI組件接收到的props是',this.props);return (<div><h1>當前求和為:{"???"}</h1><select ref={(c) => (this.selectNumber = c)}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementIfOdd}>當前求和為奇數再加</button> <button onClick={this.incrementAsync}>異步加</button> </div>);}
}
簡化版里面,容器組件更像是一個橋梁,負責連接 UI組件與Redux 的交互。所以在Container里面,創建一個count.jsx
在這個橋梁(count.jsx
)里,我們可以導入UI組件等待連接,(理論上應該導入Redux的Store,就完成了,但是不行,store必須從App里面通過props傳進來 )
我在container容器里面引入了store仍然報找不到的錯(這里是錯誤示范)
//引入Count的UI組件
import CountTemplate from "../components/CountTemplate";
// 引入store
import { store } from "../../redux/store";
// 引入connect的API
import { connect } from "reat-redux";// 導入connect,并且連接UI
export default connect()(CountTemplate);
我已經引進來store了,但是還是提示找不到store,這里不是因為我沒有在connect里連接,而是不允許這種用法。只能在調用Container組件的組件里傳值用props進去。
注意:react-redux不允許直接引入,只能從Container組件被調用的那一級組件里傳進來
App組件(調用Container的組件)。這樣就不報錯了
import React from "react";
import Count from "./pages/Count";
import "./App.css";
import store from "./redux/store";
class App extends React.Component {render() {return (<div>{/* 只能用props傳遞store進入Container組件 */}<Count store={store}></Count></div>);}
}
export default App;
省略store.subscribe
首先就是之前的store.subscribe,之前我們在Redux里面,由于組件和Redux之間沒有直接監聽更新的手段。所以這里需要手動去監聽渲染組件
// 引入React核心庫
import React from "react";
import { createRoot } from "react-dom/client";
// 引入App標簽
import App from "./App";
import store from "./redux/store";// React18版本監聽并刷新頁面
const root = createRoot(document.getElementById("root"));
root.render(<App />);//監測redux中狀態的改變,如redux的狀態發生了改變,那么重新渲染App組件
store.subscribe(() => {root.render(<App />);
});
而在React-Redux中,connect就自動集成了監聽并更新的功能,所以我們不必再手動監聽。刪掉即可,測試完美替換,不影響功能。
// 引入React核心庫
import React from "react";
import { createRoot } from "react-dom/client";
// 引入App標簽
import App from "./App";// React18版本監聽并刷新頁面
const root = createRoot(document.getElementById("root"));
root.render(<App />);
React-Redux的基本使用
上面說到,Container想接受到store,只能通過props形式傳遞,但是Container本身,并不是標簽形式的,所以就用不了props來傳遞數據。要想把Container接收到的props傳給UI組件,就只能用connect的API,通過函數返回對象的形式來傳遞
首先看這個Container組件,是沒有辦法去寫子組件傳值的
也就是<CountTemplate key1={value1} ...>
這種是沒有機會寫的
所以我們只能依靠connect傳值
//引入Count的UI組件
import CountTemplate from "../../components/Count/CountTemplate";
// 引入connect的API
import { connect } from "react-redux";// 使用connect()()創建并暴露一個Count的容器組件
export default connect()(CountTemplate);
props是key-value的形式,所以我們要來模仿這種形式
這里就采用了對象的形式,來模擬這種key-value
{key:value}
或者{key:()=>{函數體}}
也就是說,props的形式都可以模擬
修改一下Container的Count組件
//引入Count的UI組件
import CountTemplatefrom "../../components/Count/CountTemplate";
// 引入connect的API
import { connect } from "react-redux";function a() {// a函數返回的對象中的key就作為傳遞給UI組件props的key,value就作為傳遞給UI組件props的value,這個value是狀態return { key1: "value1" };
}function b() {// a函數返回的對象中的key就作為傳遞給UI組件props的key,value就作為傳遞給UI組件props的value,這個value是函數return {key2: () => {console.log("OK");},};
}
// 使用connect建并暴露一個Count的容器組件,傳a,b函數的返回值給UI組件
export default connect(a, b)(CountTemplate);
UI組件獲取參數:
在UI組件打印一下:console.log("UI組件接收到的props是", this.props);
所以想獲取就更簡單了,直接this.props.key
即可
注意,a傳值的時候有點小坑,我們應該專注于狀態的傳遞。所以優化一下
function a(state) {// 這里直接傳state即可,這個state是從App傳過來的// 所有的狀態就直接用這個state給傳過去了(注意是所有的state)// key1是自定義的,不設限return { key1: state };
}
同樣,b里如果要調用dispatch函數,本應該import store
import store from "../../redux/store";
import { createAddAction } from "../../redux/count_action";function b() {// 返回值返回value為函數的對象// add是自定義的,不設限,僅代表當前返回值的函數return {add: (number) => store.dispatch(createAddAction(number)),};
}
但是由于Container里面不應該引入store,并且dispatch可以自動傳入,所以就不用通過導入store來調用dispatch了。和上面的state一樣,自動傳入dispatch
改造后:
//這個是自定義的action函數,不是redux的函數
import { createAddAction } from "../../redux/count_action";function b(dispatch) {// 自動傳入了dispatchreturn {// 不再需要用store調用dispatch // 這種就可以簡寫了add: (number) => store.dispatch(createAddAction(number)),add: (number) => dispatch(createAddAction(number)),};
}
案例總結
demo結構,重點主要集中在Container上
這四個文件照之前的沒有任何變化,所以不贅述了
UI組件 CountTemplate.jsx
import React, { Component } from "react";export default class Count extends Component {add = () => {// add操作const { value } = this.selectNumber;// 找到傳入函數并傳參this.props.add(value * 1);};addNotOdd = () => {// 奇數add操作const { value } = this.selectNumber;if (this.props.key1 % 2 !== 0) {this.props.add(value * 1);}};addAsync = () => {// 異步add操作const { value } = this.selectNumber;setTimeout(() => {this.props.add(value * 1);}, 500);};render() {return (<div><div>當前求和:{this.props.key1}</div><select ref={(c) => (this.selectNumber = c)}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><div><button onClick={this.add}>add</button><button onClick={this.sub}>sub</button><button onClick={this.addNotOdd}>addNotOdd</button><button onClick={this.addAsync}>addAsync</button></div></div>);}
}
Container組件 Countainer.jsx
import { connect } from "react-redux";
import CountTemplatefrom "../../components/count/CountTemplate";
import { add } from "../../redux/count_action";function a(store) {return { key1: store };
}function b(dispatch) {// 允許傳遞多個函數return {// add操作的函數add: (data) => dispatch(add(data)),// sub操作的函數sub: (data) => dispatch(sub(data)),};
}// 傳入func a(負責state傳遞) func b(負責函數動作傳遞)
// 最后橋梁連接CountTemplate組件
export default connect(a, b)(CountTemplate);
App.jsx
import React from "react";
import Container from "./pages/container/Container";
import store from "./redux/store";
import { BrowserRouter } from "react-router-dom";class App extends React.Component {render() {return (<BrowserRouter>{/* 在App向Container傳遞store(props形式) */}<Container store={store}></Container></BrowserRouter>);}
}
export default App;
index.js
// 引入React核心庫
import React from "react";
import { createRoot } from "react-dom/client";
// 引入App標簽
import App from "./App";
import store from "./redux/store";// React18版本監聽并刷新頁面
const root = createRoot(document.getElementById("root"));
root.render(<App />);//監測redux中狀態的改變,如redux的狀態發生了改變,那么重新渲染App組件
store.subscribe(() => {root.render(<App />);
});
以上就可以完成計算組件
Container命名優化(引出規定的函數名)
傳值的函數可以看到,實際上,所有的state和function的參數傳遞,通過一次就可以完全傳遞完。
所以react-redux里面就提供好了函數名專門傳遞state和function,就不需要我們再去單獨定義了。更加規范了。
// 函數命名不規范
function a(store) {return { key1: store };
}
// 函數命名不規范
function b(dispatch) {// 允許傳遞多個函數return {// add操作的函數add: (data) => dispatch(add(data)),// sub操作的函數sub: (data) => dispatch(sub(data)),};
}// 使用connect建并暴露一個Count的容器組件,傳a,b函數的返回值給UI組件
export default connect(a, b)(CountUI);
connect 方法是一個連接器,用于連接容器組件和 UI 組件,它第一次執行時,接收4個參數,這些參數都是可選的,它執行的執行的結果還是返回一個函數,第二次執行接收一個 UI 組件
connect方法第一次執行時(connect()
)的四個參數:mapStateToProps
、mapDispatchToProps
、mergeProps
、options
connect方法第二次執行時傳入的UI組件connect()(UI組件)
這里先說傳state和傳方法的兩個函數
mapStateToProps函數返回的是一個對象(對象value是state),mapStateToProps用于傳遞狀態
mapDispatchToProps函數返回的是一個對象(對象value是function),mapDispatchToProps用于傳遞操作狀態的方法
這只是官方推薦的API定義方式,后面還有更簡寫的方式來傳遞state和function
官方解釋:mapStateToProps,mapDispatchToProps
具體在UI獲取時候的key,還是需要看return的對象把key定義成什么,才能用this.props來獲取key
同時之前的定時加操作是在UI組件里面做的,并沒有放在`count_action.js`里面,所以把定時加操作搬進`count_action.js`
優化之后:
Container.jsx
:
import { connect } from "react-redux";
import CountUI from "../../components/count/CountUI";
import { add, sub, addAsync } from "../../redux/count_action";// 傳遞state
function mapStateToProps(store) {return { key1: store };
}// 傳遞dispatch
function mapDispatchToProps(dispatch) {// 允許傳遞多個函數 通過dispatch通知Redux執行函數return {// add操作的函數add: (data) => dispatch(add(data)),// sub操作的函數sub: (data) => dispatch(sub(data)),// addAsync操作的函數,addAsync操作搬進action里addAsync: (data, time) => dispatch(addAsync(data, time)),};
}// 傳入func mapStateToProps(負責state傳遞) func mapDispatchToProps(負責函數動作傳遞)
// 最后傳入CountTemplate組件完成連接
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
ConuntTemplate.jsx
:
import React, { Component } from "react";export default class Count extends Component {...addAsync = () => {const { value } = this.selectNumber;this.props.addAsync(value * 1, 500);};render() {return (<div>...</div>);}
}
count_action.js
:
/*
該文件專門為Count需要用的Reducer去生成action對象,封裝好type,這樣在調用組件傳一個data進來就可以了
*/
import { ADD, SUB, ADD_ASYNC } from "./constant.js";export function add(data) {// 要返回Action對象(返回type和data數據)return { type: ADD, data };
}export function sub(data) {// 要返回Action對象(返回type和data數據)return { type: SUB, data };
}// 把這個定時操作集成在action里面 => addAsync
// setTimeout(() => {
// this.props.add(value * 1);
// }, 500);
export function addAsync(data, time) {// dispatch為自動傳入return (dispatch) => {setTimeout(() => {// 通過dispatch調用add,直接調用是不可以的dispatch(add(data));}, time);};
}
其他的文件沒啥變化,不贅述了
寫到這里其實 connect
已經比較完善了,但是你可以仔細想想 redux
的工作流程
似乎少了點什么,我們在這里調用了函數,創建了 action
對象,但是好像 store
并沒有執行 dispatch
,那是不是斷了呢?執行不了呢?
其實這里 react-redux
已經幫我們做了優化,當調用 Action Creator
的時候,會立即發送 action
給 store
而不用手動的 dispatch
。后面馬上會用到這個。
總結React-Redux的基本使用
(1).明確兩個概念:1).UI組件:不能使用任何redux的api,只負責頁面的呈現、交互等。2).容器組件:負責和redux通信,將結果交給UI組件。
(2).如何創建一個容器組件————靠react-redux 的 connect函數connect(mapStateToProps,mapDispatchToProps)(UI組件)-mapStateToProps:映射狀態,返回值是一個對象(對象以k:v形式保存state)-mapDispatchToProps:映射操作狀態的方法,返回值是一個對象(對象以k:v形式保存多個方法)
(3).備注1:容器組件中的store是靠props傳進去的,而不是在容器組件中直接引入
(4).備注2:mapDispatchToProps,也可以是一個對象(只傳遞一個方法,如果多個方法就需要多個k:v對象)
React-Redux優化
之前的案例,很多功能都是雜糅到一起的,如果體量大起來會讓人不知道該怎么辦,所以這里在這個案例里面對之前的功能代碼進行拆分操作,分文件處理。優化文件結構。
前置知識
例子1:
function mapStateToProps(store) {return { key1: store };}
可以用箭頭函數來寫
const mapStateToProps = (store)=> {return { key1: store };}
如果箭頭函數只有一句并且默認return只有一個對象,就可以省略return,直接箭頭指一個括號,就代表return值了 ({ key1: store })
最后優化完
const mapStateToProps = (store)=> ({ key1: store })
例子2:
function mapDispatchToProps(dispatch) {// 多個方法傳遞return {add: (data) => dispatch(add(data)),sub: (data) => dispatch(sub(data)),addAsync: (data, time) => dispatch(addAsync(data, time)),};
}
按照上面的說法,反正只有一個return,這里就可以直接優化成
mapDispatchToProps=(dispatch)=>{add: (data) => dispatch(add(data)),sub: (data) => dispatch(sub(data)),addAsync: (data, time) => dispatch(addAsync(data, time)),}
帶入到connect里面,甚至不用寫屬性,直接把函數體丟進去即可
簡寫 mapStateToProps & mapDispatchToProps
對于connect來說,第一次調用connect()
,傳入什么名字的函數不重要,傳入的位置很重要,connect只認位置上的傳入的值。
簡寫完前后對比:
/*
// 傳統寫法function mapStateToProps(store) {return { key1: store };}function mapDispatchToProps(dispatch) {// 多個方法傳遞return {add: (data) => dispatch(add(data)),sub: (data) => dispatch(sub(data)),addAsync: (data, time) => dispatch(addAsync(data, time)),};}
*/// 引入connect生成一個容器組件,連接好React-Redux和UI組件,并暴露
// UI組件中依舊是通過this.props.xxxxxxx讀取和操作狀態
// export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
export default connect(// 對于第一個參數位置來說,首次調用可以傳四個參數[ mapStateToProps 、mapDispatchToProps 、mergeProps、options ]//store沒有React-Redux給你自動調用,所以這里要自己寫(state) => ({ count: state }), // 等價于mapStateToProps //mapDispatchToProps的簡寫(key:函數名),甚至不需要標注參數列表//dispatch有React-Redux給你自動調用,所以這里不用寫了{ add: add, sub: sub, addAsync: addAsync }//等價于mapDispatchToProps
)(CountUI);
完整開發
目錄結構:
首先就是把Contrainer和UI組件放在一起,合并成一個Container文件叫Count。雖然文件是在一起,但是功能和類都是分開的。只是文件放一起了,對外依舊暴露Container,UI這次徹底不用export了。
import { connect } from "react-redux";
import { add, sub, addAsync } from "../../redux/count_action";
import React, { Component } from "react";// 這次CountUI徹底不用暴露了,暴露的任務交給connect來做。connect生成一個對外的Container,同時包含了數據和UI組件
// export default class Count extends Component {
class CountUI extends Component {add = () => {const { value } = this.selectNumber;this.props.add(value * 1);};sub = () => {const { value } = this.selectNumber;if (this.props.key1 === 0 || this.props.key1 < value) {return;}this.props.sub(value * 1);};addNotOdd = () => {const { value } = this.selectNumber;if (this.props.key1 % 2 !== 0) {this.props.add(value * 1);}};addAsync = () => {const { value } = this.selectNumber;this.props.addAsync(value * 1, 500);};render() {console.log(this.props);return (<div><div>當前求和:{this.props.key1}</div><select ref={(c) => (this.selectNumber = c)}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><div><button onClick={this.add}>add</button><button onClick={this.sub}>sub</button><button onClick={this.addNotOdd}>addNotOdd</button><button onClick={this.addAsync}>addAsync</button></div></div>);}
}/*function mapStateToProps(store) {return { key1: store };}function mapDispatchToProps(dispatch) {// 多個方法傳遞return {add: (data) => dispatch(add(data)),sub: (data) => dispatch(sub(data)),addAsync: (data, time) => dispatch(addAsync(data, time)),};}
*/// 引入connect生成一個容器組件,連接好React-Redux和UI組件,并暴露
// UI組件中依舊是通過this.props.xxxxxxx讀取和操作狀態
// export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
export default connect((state) => ({ key1: state }), // 狀態{ add: add, sub: sub, addAsync: addAsync } // 方法(不再需要注明參數列表)
)(CountUI);
Provider組件使用
首先看個之前的store傳遞,很麻煩,需要手動傳遞,如果每個標簽都傳遞,都需要使用 store 時,很麻煩的
<Count store={store}/>
{/* 示例 */}
<Demo1 store={store}/>
<Demo1 store={store}/>
<Demo1 store={store}/>
<Demo1 store={store}/>
<Demo1 store={store}/>
所以官方就提供了一個大組件Provider,自動可以尋找需要用store的標簽,自動精準傳遞store進去
我們可以這么做:在 src 目錄下的 index.js
文件中,引入 Provider
,直接用 Provider
標簽包裹 App
組件,將 store
寫在 Provider
中即可
import { Provider } from "react-redux";ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById("root")
);
這樣我們在 App.jsx
文件中,組件無需手寫指定 store
,即可使用 store
非常方便
總結React-Redux優化
1.容器組件和UI組件整合一個文件
2.若有多個容器組件,無需自己給每個容器組件傳遞store,給包裹一個<Provider store={store}>
即可。整體就貢獻同一個store了。
3.使用了react-redux后也不用再自己檢測redux中狀態的改變了,容器組件可以自動完成這個工作。
4.mapDispatchToProps也可以簡單的寫成一個對象,因為react-redux可以自動dispatch
5.一個組件要和react-redux“打交道”要經過哪幾步?
- 1)定義好UI組件—不暴露
- 2)引入connect生成一個容器組件,并暴露,寫法如下:
connect(state => ({key:value}), //映射狀態{key:xxxxxAction} //映射操作狀態的方法
)(UI組件)
- 3)在UI組件中通過
this.props.定義好的key名
讀取和操作狀態
React-Redux綜合案例(多組件組合共享)
引言
在寫完了基本的 Redux 案例之后,我們可以嘗試一些更實戰性的操作,比如我們可以試試多組件間的狀態傳遞,相互之間的交互
如上動圖所示,我們想要實現上面的案例,采用純 React 來實現是比較困難的,我們需要很多層的數據交換才能實現,但是我們如果采用 Redux 來實現會變得非常簡單
因為 Redux 打通了組件間的隔閡,完成了復雜組件的數據交互,我們可以自由的進行數據交換,所有存放在 store
中的數據都可以實現共享,那我們接下來看看如何實現的吧~
整合UI組件與容器組件
如果UI組件和容器組件都分開寫,那么實際上文件數量就得成倍增長,為了優化就整合一下兩個文件,把UI組件和容器組件 放在一起。實際開發中也是整合的。
首先,一個jsx文件里面可以定義多個組件,其實思想上還是容器組件往UI組件傳props,只不過兩個組件放在了同一個文件里,對外只暴露一個Container接口。
這里演示一下兩個或者多個組件在同一個文件里
創建一個Test組件
import React, { Component } from "react";// 默認對外暴露的容器組件
export default class Test extends Component {render() {return <div>Test</div>;}
}// UI組件,不對外暴露,但是可以在自己文件里面用
class Test2 extends Component {render() {return <div>UI組件</div>;}
}
// 他倆之間的連接靠connect
// 這是個簡單的connect,后面有詳細的描述
export default connect((state) => ({對象}),{// action動作}
)(CountUI);
所以以后就可以只留一個Container文件夾放容器組件
UI組件只要想去拿Redux的狀態,就直接找內部的connect,因為這個connect已經拿到了Redux的所有state
后面有詳細的描述
重新安排Redux文件結構
如果有Person和Count的組件,每個組件對應的action和reducer這兩個文件,都是成倍的增長,所以肯定不能這么一大堆羅列于此,就要把Redux文件夾分層
分層后的Redux,將action文件和reducer文件分開
就不用再叫 組件_action.js
或 組件_reducer.js
的這種了,因為已經在action文件夾下面了,根據文件夾就能分辨出來
React-Redux結構 & 合并Reducer
React-Redux結構
Redux里用的存儲結構:
如果是只存了一個值,比如數字這種,那就是不用key去取,直接就this.store即代表著當前對象。
如果是存了兩個值以上,n個對象,那就是Redux作為一個大對象,里面存了n多個小對象,通過key來獲取目標對象(key是合并reducer時定義的)
合并Reducer
之前的store中只注冊了一個Reducer并導出,對于多個Reducer來說,只導出一個肯定是不行的,所以我們要將其他Reducer也注冊到store里面,并且命名好key來幫助后續獲取
這里提一個前置知識:combineReducers({對象})
傳入的對象,就是Redux保存的總對象
重要事情說三遍~
combineReducers({對象})
傳入的所有對象,就是Redux保存的總對象
combineReducers({對象})
傳入的所有對象,就是Redux保存的總對象
combineReducers({對象})
傳入的所有對象,就是Redux保存的總對象
合并后的store.js:
//引入store創建,以及中間件
import { combineReducers,createStore } from "redux";
//引入為store服務的Reducer
import countReducer from "../redux/reducer/count";
import personReducer from "../redux/reducer/person";// 之前的store只能注冊一個reducer,也就是count的countReducer
// 沒法注冊person的reducer,所以我們需要用函數把兩個函數都注冊了
// const store = createStore(countReducer, applyMiddleware(thunk));// 合并
const allReducer = combineReducers({// sum就是countReducer的key,sum: countReducer,// persons就是personReducer的key,persons: personReducer,
});
//全局只暴露這個Reducers,同樣需要用createStore函數創建
export default createStore(allReducer);
Container通過key獲取對應的狀態
Count組件的connect
// 使用connect建并暴露一個Count的容器組件,傳a,b函數的返回值給UI組件
/*(state) => ({ sumNumber: state.sum })對應的含義:(默認傳入的全局Redux對象) => ({ 自定義的在UI組件使用的名字: 默認傳入的全局Redux對象.目標對象的key })
*/
export default connect(// 這里還獲取了Person組件的人數長度(通過Redux獲取其他組件的值)// 這里可以傳入多個 k-v ,用逗號分隔(state) => ({ sumNumber: state.sum, personLength: state.persons.length }),{// action動作和以前一樣傳遞即可add: add,sub: sub,addAsync: addAsync,}
)(CountUI);
Person組件的connect
/*(state) => ({ personList: state.persons }),對應的含義:(默認傳入的全局Redux對象) => ({ 自定義的在UI組件使用的名字: 默認傳入的全局Redux對象.目標對象的key }),
*/
export default connect(// 這里還獲取了Count組件的求和值(通過Redux獲取其他組件的值)// 這里可以傳入多個 k-v ,用逗號分隔(state) => ({ personList: state.persons, countSum: state.sum }),{// 這里綁定的action該咋傳咋傳addPerson: addPerson,}
)(PersonUI);
整個案例代碼
先看結構:
容器組件Count.jsx:
// 引入connect的API
import { connect } from "react-redux";
import { add, sub, addAsync } from "../../redux/action/count";import React, { Component } from "react";class CountUI extends Component {add = () => {const { value } = this.count;this.props.add(value);};sub = () => {const { value } = this.count;this.props.sub(value);};addNotOdd = () => {if (this.props.sumNumber % 2 !== 0) {this.props.add(value);}};addAsync = () => {const { value } = this.count;this.props.addAsync(value, 500);};render() {console.log(this.props);return (<div><h1>當前結果{this.props.sumNumber},下方組件總人數為{this.props.personLength}</h1>選擇內容<select ref={(c) => (this.count = c)}><option value={1}>1</option><option value={2}>2</option><option value={3}>3</option></select><button onClick={this.add}>+</button><button onClick={this.sub}>-</button><button onClick={this.addNotOdd}>當前奇數加</button><button onClick={this.addAsync}>非同步加</button></div>);}
}// 使用connect建并暴露一個Count的容器組件,傳a,b函數的返回值給UI組件
/*(state) => ({ sumNumber: state.sum })對應的含義:(默認傳入的全局Redux對象) => ({ 自定義的在UI組件使用的名字: 默認傳入的全局Redux對象.目標對象的key })
*/
export default connect(// 這里還獲取了Person組件的人數長度(通過Redux獲取其他組件的值)// 這里可以傳入多個 k-v ,用逗號分隔(state) => ({ sumNumber: state.sum, personLength: state.persons.length }),{// action動作和以前一樣傳遞即可add: add,sub: sub,addAsync: addAsync,}
)(CountUI);
容器組件Person.jsx:
import React, { Component } from "react";
import { addPerson } from "../../redux/action/person";
import { nanoid } from "nanoid";
import { connect } from "react-redux";class PersonUI extends Component {addPerson = () => {const name = this.name.value;const age = this.age.value;if (name === "" || age === "") {return;}const newPeson = { id: nanoid(), name, age };this.props.addPerson(newPeson);};render() {return (<div><h1>我是person組件,上方組件求和為:{this.props.countSum}</h1><inputref={(name) => {this.name = name;}}></input><inputref={(age) => {this.age = age;}}></input><button onClick={this.addPerson}>add New Person</button><ul>{this.props.personList.map((p) => {return (<li key={p.id}>name: {p.name}---- age: {p.age}</li>);})}</ul></div>);}
}/*(state) => ({ personList: state.persons }),對應的含義:(默認傳入的全局Redux對象) => ({ 自定義的在UI組件使用的名字: 默認傳入的全局Redux對象.目標對象的key }),
*/
export default connect(// 這里還獲取了Count組件的求和值(通過Redux獲取其他組件的值)// 這里可以傳入多個 k-v ,用逗號分隔(state) => ({ personList: state.persons, countSum: state.sum }),{// 這里綁定的action該咋傳咋傳addPerson: addPerson,}
)(PersonUI);
Action下的count.js:
import { ADD, SUB } from "../constant.js";
import store from "../store.js";export const add = (data) => ({ type: ADD, data });
export const sub = (data) => ({ type: SUB, data });// 異步action,就是只action的值為函數,在異步action中,一般都會調用同步action,異步action不是必須要用的
// 能在這里寫函數,是因為在store做了redux-thunk的設置
export const addAsync = (data, time) => {return () => {// 這里其實只套了一個定時操作setTimeout(() => {// 調用已經定義好的增加actionstore.dispatch(add(data));}, time);};
};
Action下的person.js:
import { ADD_PERSON } from "../constant";export const addPerson = (data) => ({ type: ADD_PERSON, data });
reducer下的count.js:
import { ADD, SUB } from "../constant.js";export default function countReducer(previousState, action) {// 從Action對象里傳來的action對象,里面包含type和data,所以我們需要解構出來。等待后續處理const { type, data } = action;if (previousState === undefined) {return 0;}// 判斷傳入功能類型switch (type) {case ADD://這里用原來的值+新傳入的值,得到新值return previousState + data * 1;case SUB:if (previousState < data * 1) {return previousState;}//這里用原來的值-新傳入的值,得到新值return previousState - data * 1;// 提一嘴,這里都是return,所以不用breakdefault:return previousState;}
}
reducer下的person.js:
import { ADD_PERSON } from "../constant.js";const initPersonList = [{ id: "123456", name: "Tom", age: "20" }];export default function personReducer(previousState = initPersonList, action) {const { type, data } = action;switch (type) {case ADD_PERSON:// 把之前的展開(之前的數組是沒展開的),把新來的加進去return [data, ...previousState];default:return previousState;}
}
常量constant.js
export const ADD = "add";
export const SUB = "sub";
export const ADD_PERSON = "addPerson";
store.js
//引入store創建,以及中間件
import { applyMiddleware, combineReducers, createStore } from "redux";
//引入為store服務的Reducer
import countReducer from "../redux/reducer/count";
import personReducer from "../redux/reducer/person";
import thunk from "redux-thunk";// 之前的store只能注冊一個reducer,也就是count的countReducer
// 沒法注冊person的reducer,所以我們需要用函數把兩個函數都注冊了
// const store = createStore(countReducer, applyMiddleware(thunk));// 合并
const allReducer = combineReducers({// sum就是countReducer的key,sum: countReducer,// persons就是personReducer的key,persons: personReducer,
});// 全局只暴露這個Reducers,同樣需要用createStore函數創建
// 同時允許接收函數的applyMiddleware(thunk)也不能丟了
export default createStore(allReducer, applyMiddleware(thunk));
App.jsx
class App extends React.Component {render() {return (<div><Provider store={store}>{/* 用Provider組件的props傳遞store進入Container組件 */}// 被Provider組件包裹的子組件都能自動接收到store// 這倆都是容器組件<Count></Count><Person></Person></Provider></div>);}
}
export default App;
發現個小bug:
原因是沒有弄redux-thunk做中間件支持,再詳細可以看這個:redux-thunk
總結React-Redux綜合案例
(1).定義一個Pserson組件,和Count組件通過redux共享數據。
(2).為Person組件編寫:reducer、action,配置constant常量。
(3).重點:Person的reducer和Count的Reducer要使用combineReducers進行合并,合并后的總狀態是一個對象!!!
(4).交給store的是總reducer,最后注意在組件中取出狀態的時候,記得“取到位”。
純函數概念
就是一個概念,沒有編碼
引言
1.一類特別的函數: 只要是同樣的輸入(實參),必定得到同樣的輸出(返回)
2.必須遵守以下一些約束
- 1)不得改寫參數數據
- 2)不會產生任何副作用,例如網絡請求,輸入和輸出設備
- 3)純函數里面不能調用Date.now()或者Math.random()等不純的方法
3.redux的reducer函數必須是一個純函數
例子
純函數要求輸入和輸出相同
比如:
// a是純函數
function a(data) {return data;
}
// b不是純函數
function b(data) {let c = 1;return c;
}
再拿一個reducer舉個例子:
這里的 return [data, ...previousState];
。因為地址引用發生了變化,所以就能觸發頁面更新。因為Redux是淺比較,不比較對象內容,發現return的引用地址變化了就自動更新了。
export default function personReducer(previousState = initPersonList, action) {const { type, data } = action;switch (type) {case ADD_PERSON:// 把之前的展開(之前的數組是沒展開的),把新來的加進去return [data, ...previousState];default:return previousState;}
}
如果是非純函數,比如這樣。這就無法生效,因為不是純函數
export default function personReducer(previousState = initPersonList, action) {const { type, data } = action;
switch (type) {case ADD_PERSON:var newArray = previousState;newArray.push(data);return newArray;default:return previousState;}
}
Redux-DevTools
顧名思義,開發者工具
需要插件+npm庫的依賴
npm install redux-devtools-extension --legacy-peer-deps
store需要導入redux-devtools-extension的依賴,并且在暴露store的connect函數的第二個參數位置傳入對應的api
如果之前有applyMiddleware(thunk)
的中間件操作,可以選擇將中間件傳入其api
...省略
// redux-devtools api
import { composeWithDevTools } from "redux-devtools-extension";// 合并
const allReducer = combineReducers({// sum就是countReducer的key,sum: countReducer,// persons就是personReducer的key,persons: personReducer,
});export default createStore(allReducer,// redux-devtools apicomposeWithDevTools(applyMiddleware(thunk))
);
再次啟動項目,就可以在瀏覽器的插件里面看redux存進去的東西了
總結工具使用
(1).yarn add redux-devtools-extension
(2).store中進行配置import {composeWithDevTools} from 'redux-devtools-extension'const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
Reducers優化
之前store中,所有的Reducers都是積壓在store中進行的合并,如果是大型項目將難以維護,所以我們一般把Reducers的合并,放在一個單獨的js文件中去做,完成導出,導出完畢之后在store中引入即可
在reducer下專門新建一個index文件,完成匯總合并操作
/*該文件用于匯總所有的reducer為一個總的reducer
*/
import { combineReducers } from "redux";//引入為store服務的Reducer
import countReducer from "./count";
import personReducer from "./person";
// 合并
const allReducer = combineReducers({// sum就是countReducer的key,sum: countReducer,// persons就是personReducer的key,persons: personReducer,
});// 導出
export default allReducer;
精簡之后的store文件,非常清爽
//引入store創建,以及中間件
import { applyMiddleware, createStore } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";// 引入reducers
import allReducer from "./reducer";export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk))
);
總結:
(1).所有變量名字要規范,盡量觸發對象的簡寫形式。
(2).reducers文件夾中,編寫index.js專門用于匯總并暴露所有的reducer