06-React組件 Redux React-Redux

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 來實現

  1. 某個組件的狀態需要共享時
  2. 一個組件需要改變其他組件的狀態時
  3. 一個組件需要改變全局的狀態時

除此之外,還有很多情況都需要使用 Redux 來實現(還沒有學 hook,或許還有更好的方法)

在這里插入圖片描述

Redux工作流程

  • store

store 是 Redux 的核心,可以理解為是 Redux 的數據流向指揮,我們可以將任何我們想要存放的數據放在 store 中,在我們需要使用這些數據時,我們可以從中取出相應的數據。因此我們需要先創建一個 store ,在 Redux 中可以使用 createStore API 來創建一個 store

store是無法直接進行操作的,需要借助Redux進行操作
store是一個調度者,store是指揮者,不干活。需要任何操作,或者動作,都會去分發走,找到對應的擔當來做。(如果越過了store,action直接去找reducers,就有點類似去餐廳點餐,不找前臺點餐,直奔后廚要吃的😂)

  • action

actionstore 中唯一的數據來源,一般來說,我們會通過調用 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>&nbsp;<button onClick={this.increment}>+</button>&nbsp;<button onClick={this.decrement}>-</button>&nbsp;<button onClick={this.incrementIfOdd}>當前求和為奇數再加</button>&nbsp;<button onClick={this.incrementAsync}>異步加</button>&nbsp;</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())的四個參數:mapStateToPropsmapDispatchToPropsmergePropsoptions
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 的工作流程

image-20210909194900532

似乎少了點什么,我們在這里調用了函數,創建了 action 對象,但是好像 store 并沒有執行 dispatch ,那是不是斷了呢?執行不了呢?

其實這里 react-redux 已經幫我們做了優化,當調用 Action Creator 的時候,會立即發送 actionstore 而不用手動的 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-demo

如上動圖所示,我們想要實現上面的案例,采用純 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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/212870.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212870.shtml
英文地址,請注明出處:http://en.pswp.cn/news/212870.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

計算機網絡測試題第二部分

前言:如果沒有做在線測試請自主獨立完成&#xff0c;本篇文章只作為學習計算機網絡的參考&#xff0c;題庫中的題存在一定錯誤和不完整&#xff0c;請學習時&#xff0c;查找多方書籍論證&#xff0c;獨立思考&#xff0c;如果存在疑慮可以評論區討論。查看時&#xff0c;請分清…

pytorch debug 常用工具

自動辨識圖像格式可視化 import numpy as np import matplotlib.pyplot as plt from PIL import Imagedef convert_to_numpy(image_input):"""自動檢測輸入圖像類型&#xff0c;并將其轉換為NumPy數組。"""if isinstance(image_input, np.ndarr…

7-3 Left-pad

根據新浪微博上的消息&#xff0c;有一位開發者不滿NPM&#xff08;Node Package Manager&#xff09;的做法&#xff0c;收回了自己的開源代碼&#xff0c;其中包括一個叫left-pad的模塊&#xff0c;就是這個模塊把javascript里面的React/Babel干癱瘓了。這是個什么樣的模塊&a…

物聯網IC

物聯網IC 電子元器件百科 文章目錄 物聯網IC前言一、物聯網IC是什么二、物聯網IC的類別三、物聯網IC的應用實例四、物聯網IC的作用原理總結前言 物聯網IC的功能和特性可以根據不同的物聯網應用需求來選擇和配置,以滿足物聯網設備在連接、通信、感知和控制方面的需求。 一、物…

猜數字游戲Ⅱ

你和朋友一起玩猜數字游戲&#xff0c;你寫出一個秘密數字&#xff0c;請朋友猜這個數字是多少。朋友每猜測一次&#xff0c;你就會給他一個包含下述信息的提示&#xff1a; 猜測數字中有多少位屬于數字和確切位置都猜對了&#xff08;稱為 "Bulls"&#xff0c;公牛&…

VOL-vue 框架 文件上傳控件關于大文件上傳等待的修改

我的項目在測試voltable列表組件中對阿里云OSS做附件上傳時&#xff0c;幾十M的文件可能就會需要一段時間來上傳&#xff0c;才能有OSS的狀態和鏈接返回。 但是控件VolUpload.vue并沒有去在這方面做任何交互體驗上的控制&#xff0c;而且VolUpload.vue本身寫的幾個上傳函數都是…

SpringBoo在項目停止(服務停止/關閉退出)之后執行的方法

SpringBoo在項目停止/服務停止/關閉退出之后執行的方法 1.實現DisposableBean接口2.使用PreDestroy注解 SpringApplication會向JVM注冊一個關閉鉤子(hook)&#xff0c;以確保ApplicationContext在退出時正常關閉。 可以使用所有標準的Spring生命周期回調&#xff08;例如Dispos…

內測分發是什么?十年的前端開發者帶你了解

內測分發是軟件開發過程中的一個階段&#xff0c;特別指軟件還未完全完成或準備對外廣泛發布前&#xff0c;向一定范圍的用戶群體提供該軟件版本的測試機會&#xff0c;以便收集反饋和修復潛在的問題。在講解內測分發之前&#xff0c;我們需要明確幾個相關概念&#xff1a; 軟件…

區塊鏈媒體宣發:揭示優勢與趨勢,引領信息傳播新時代

在數字化潮流中&#xff0c;區塊鏈技術正以驚人的速度改變著傳媒行業的格局。從區塊鏈媒體宣發中獲得的種種優勢和未來的趨勢&#xff0c;不僅為企業帶來了新的推廣途徑&#xff0c;也在信息傳播領域掀起了一場革命。本文將深入探討區塊鏈媒體宣發的優勢以及未來的發展趨勢。 1…

排序算法---選擇排序

1.實現流程&#xff1a; 1. 把第一個沒有排序過的元素設置為最小值&#xff1b; 2. 遍歷每個沒有排序過的元素&#xff1b; 3. 如果元素 < 現在的最小值&#xff1b; 4. 將此元素設置成為新的最小值&#xff1b; 5. 將最小值和第一個沒有排序過的位置交換 選擇排序執行流程…

初識Ceph --組件、存儲類型、存儲原理

目錄 ceph組件存儲類型塊存儲文件存儲對象存儲 存儲過程 ceph Ceph&#xff08;分布式存儲系統&#xff09;是一個開源的分布式存儲系統&#xff0c;設計用于提供高性能、高可靠性和可擴展性的存儲服務&#xff0c;可以避免單點故障&#xff0c;支持塊存儲、對象存儲以及文件系…

【小白專用】Apache2.4+PHP8.3+MYSQL的配置

1.下載PHP和Apache 1、PHP下載 PHP For Windows: Binaries and sources Releases 注意&#xff1a; 1.使用Apache作為服務器的話&#xff0c;一定要下載Thread Safe的&#xff0c;否則沒有php8apache2_4.dll這個文件&#xff0c; 如果使用IIS的請下載 NON Tread safe的 2.如果…

USB連接器

USB連接器 電子元器件百科 文章目錄 USB連接器前言一、USB連接器是什么二、USB連接器的類別三、USB連接器的應用實例四、USB連接器的作用原理總結前言 USB連接器的使用廣泛,幾乎所有現代電子設備都具備USB接口,使得設備之間的數據傳輸和充電變得簡單和便捷。 一、USB連接器是…

element-ui按鈕el-button,點擊之后恢復之前的顏色

在開發過程中, 使用el-button 按鈕點擊之后, 沒有恢復到之前的顏色, 還是保持點擊之后的顏色,需要解決這個問題, <template><div><el-button size"mini" type"primary" plain click"onClick($event)">按鈕</el-button>…

iOS按鈕控件UIButton使用

1.在故事板中添加按鈕控件,步聚如下: 同時按鈕Shift+Commad+L在出現在控件庫中選擇Button并拖入View Controller Scene中 將控件與變量btnSelect關聯 關聯后空心變實心 如何關聯?直接到屬性窗口拖按鈕變量到控件上,出現一條線,然后松開,這樣就關聯成功了 關聯成功后屬性窗口…

LinuxBasicsForHackers筆記 -- 了解和檢查無線網絡

無線網絡 AP (access point) – 無線用戶連接以訪問互聯網的設備。SSID (service set identifier) – 網絡的名稱。ESSID (extended service set identifier) – 與 SSID 相同&#xff0c;但它可用于無線 LAN 中的多個 AP。BSSID (basic service set identifier) – 每個AP的唯…

ISP IC/FPGA設計-第一部分-MT9V034攝像頭分析(0)

MT9V034為CMOS圖像傳感器&#xff0c;有著極其優秀的圖像成像性能&#xff0c;同時支持豐富的功能用于isp的開發&#xff1b;MT9V034 的HDR寬動態、10bit數據深度、RAW格式&#xff08;bayer陣列&#xff09;圖像、dvp和lvds接口、60fps正是學習isp開發的理想傳感器&#xff1b…

使用Git進行版本控制

參考&#xff1a;《Python編程從入門到實踐》 前言1、安裝、配置 Git1.1 在Linux系統中安裝Git1.2 在OS X系統中安裝Git1.3 在Windows系統中安裝Git1.4 配置Git 2、創建項目3、忽略文件4、初始化倉庫5、檢查狀態6、將文件加入到倉庫中7、執行提交8、查看提交歷史 前言 版本控制…

C語言 預處理 + 條件編譯宏 + 井號運算符

預處理階段任務 預處理指令 條件編譯宏 條件編譯宏的作用在于根據編譯時的條件進行代碼的選擇性編譯&#xff0c;從而實現不同環境、不同配置或不同功能的編譯版本。 這可以用于實現調試模式和發布模式的切換&#xff0c;平臺適配&#xff0c;以及選擇性地編譯不同的功能模塊等…

Git merge 與 Git rebase 與 Git fetch

Git merge 與 Git rebase 看這個圖就行了 git merge、git rebase 和 git fetch 是 Git 中的三個不同的命令&#xff0c;它們分別用于不同的目的。以下是它們的主要區別&#xff1a; git merge&#xff08;合并&#xff09;&#xff1a; 用途&#xff1a; 用于將一個分支的更改…