一.引入:
react本身是一個非常輕量級的視圖層框架命斧,因為組件傳值太麻煩了。在react基礎上配套一個數(shù)據(jù)層的框架和他一起結合使用,才能做大型項目脂崔。用Redux,組件傳值變簡單梧喷,redux要求把數(shù)據(jù)都放在一個公共的存儲區(qū)域Store砌左,組件之中盡量少放數(shù)據(jù),所有組建的數(shù)據(jù)都不放在組件自身铺敌,都放在公用的存儲空間里面store這里汇歹。想要改變數(shù)據(jù)傳遞給其他組件,一個組件只需要改變store里面對應的數(shù)據(jù)就行了偿凭,接著其他組件會自動感知到store有變化产弹,store只要一有變化,其他組件就會自動的去store里面重新取數(shù)據(jù)弯囊,這樣就能非常輕松的在租件之間傳值痰哨,簡化數(shù)據(jù)的傳遞胶果。間接的實現(xiàn)在組件之間傳遞數(shù)據(jù)的功能。
Redux=Reducer+Flux//Flux實際上就是官方推出的最原始的一個輔助react使用的數(shù)據(jù)層框架斤斧,他的公共數(shù)據(jù)存儲區(qū)域store這個部分可以由很多的store組成早抠,這樣數(shù)據(jù)存儲的時候可能存在數(shù)據(jù)依賴的問題,不是很好用撬讽。于是把Flux升級成Redux蕊连, Redux在引用了很多Flux的概念之外又引入了reducer這個概念。
二. Redux的工作流程(Redux是數(shù)據(jù)層的框架游昼,把所有數(shù)據(jù)放在Store之中)
Redux是解決數(shù)據(jù)傳遞問題的一個框架咪奖,把所有數(shù)據(jù)放在Store之中進行管理。
- React Components:借書的用戶
- Action Creators:你說的想要借什么書這句話
- Store:圖書館的管理員酱床,最重要的一個環(huán)節(jié)羊赵,優(yōu)先編寫這個環(huán)節(jié)
-
Reducers:書放在哪里的記錄本
改變store的數(shù)據(jù)是一樣的:你的組件先跟store說我要改變你的數(shù)據(jù)(通過Action Creators說)鸯绿,Store知道后跋破,去問Reducers,store的數(shù)據(jù)應該如何被修改瓶蝴,store數(shù)據(jù)修改好之后告訴組件說數(shù)據(jù)修改完了可以重新獲取一下數(shù)據(jù)毒返,組件再來獲取數(shù)據(jù)可以取到最新的數(shù)據(jù)。
首先有一個組件,要去獲取Store中的一些數(shù)據(jù)扇谣,跟Store說我要獲取一些數(shù)據(jù)這句話就是Action Creators昧捷,Action Creators創(chuàng)建了一句話之后,告訴了Store罐寨,Store接收到你要一些數(shù)據(jù)之后得去Reducers查一下應該給組件什么數(shù)據(jù)靡挥,Store知道后把對應的數(shù)據(jù)給到組件就可以了。
首先舷手,用戶發(fā)出 Action拧簸。
store.dispatch(action);
然后,Store 自動調用 Reducer男窟,并且傳入兩個參數(shù):當前 State 和收到的 Action盆赤。 Reducer 會返回新的 State 。
let nextState = todoApp(previousState, action);
State 一旦有變化歉眷,Store 就會調用監(jiān)聽函數(shù)牺六。
// 設置監(jiān)聽函數(shù)
store.subscribe(listener);
listener可以通過store.getState()得到當前狀態(tài)。如果使用的是 React汗捡,這時可以觸發(fā)重新渲染 View淑际。
function listerner() {
let newState = store.getState();
component.setState(newState);
}
三、基本概念和 API
3.1 Store
Store 就是保存數(shù)據(jù)的地方,你可以把它看成一個容器庸追。整個應用只能有一個 Store霍骄。
Redux 提供createStore
這個函數(shù),用來生成 Store淡溯。
import { createStore } from 'redux'; const store = createStore(fn);
上面代碼中读整,createStore
函數(shù)接受另一個函數(shù)作為參數(shù),返回新生成的 Store 對象咱娶。
3.2 State
Store
對象包含所有數(shù)據(jù)米间。如果想得到某個時點的數(shù)據(jù),就要對 Store 生成快照膘侮。這種時點的數(shù)據(jù)集合屈糊,就叫做 State。
當前時刻的 State琼了,可以通過store.getState()
拿到逻锐。
import { createStore } from 'redux'; const store = createStore(fn); const state = store.getState();
Redux 規(guī)定, 一個 State 對應一個 View雕薪。只要 State 相同昧诱,View 就相同。你知道 State所袁,就知道 View 是什么樣盏档,反之亦然。
3.3 Action
State 的變化燥爷,會導致 View 的變化蜈亩。但是,用戶接觸不到 State前翎,只能接觸到 View稚配。所以,State 的變化必須是 View 導致的鱼填。Action 就是 View 發(fā)出的通知药有,表示 State 應該要發(fā)生變化了。
Action 是一個對象苹丸。其中的type
屬性是必須的,表示 Action 的名稱苇经。其他屬性可以自由設置赘理,社區(qū)有一個規(guī)范可以參考。
const action = { type: 'ADD_TODO', payload: 'Learn Redux' };
上面代碼中扇单,Action 的名稱是ADD_TODO
商模,它攜帶的信息是字符串Learn Redux
。
可以這樣理解,Action 描述當前發(fā)生的事情施流。改變 State 的唯一辦法响疚,就是使用 Action。它會運送數(shù)據(jù)到 Store瞪醋。
3.4 Action Creator
View 要發(fā)送多少種消息忿晕,就會有多少種 Action。如果都手寫银受,會很麻煩践盼。可以定義一個函數(shù)來生成 Action宾巍,這個函數(shù)就叫 Action Creator咕幻。可以拆分出actionTypes寫在actionTypes.js里面
const ADD_TODO = '添加 TODO'; function addTodo(text) { return { type: ADD_TODO, text } } const action = addTodo('Learn Redux');
上面代碼中顶霞,addTodo
函數(shù)就是一個 Action Creator肄程。
3.5 store.dispatch()
store.dispatch()
是 View 發(fā)出 Action 的唯一方法。
import { createStore } from 'redux'; const store = createStore(fn); store.dispatch({ type: 'ADD_TODO', payload: 'Learn Redux' });
上面代碼中选浑,store.dispatch
接受一個 Action 對象作為參數(shù)绷耍,將它發(fā)送出去。
結合 Action Creator鲜侥,這段代碼可以改寫如下褂始。
store.dispatch(addTodo('Learn Redux'));
3.6 Reducer
Store 收到 Action 以后,必須給出一個新的 State描函,這樣 View 才會發(fā)生變化崎苗。這種 State 的計算過程就叫做 Reducer。
Reducer 是一個函數(shù)舀寓,它接受 Action 和當前 State 作為參數(shù)胆数,返回一個新的 State。
const reducer = function (state, action) { // ... return new_state; };
整個應用的初始狀態(tài)互墓,可以作為 State 的默認值必尼。下面是一個實際的例子。
const defaultState = 0; const reducer = (state = defaultState, action) => { switch (action.type) { case 'ADD': return state + action.payload; default: return state; } }; const state = reducer(1, { type: 'ADD', payload: 2 });
上面代碼中篡撵,reducer
函數(shù)收到名為ADD
的 Action 以后判莉,就返回一個新的 State,作為加法的計算結果育谬。其他運算的邏輯(比如減法)券盅,也可以根據(jù) Action 的不同來實現(xiàn)。
實際應用中膛檀,Reducer 函數(shù)不用像上面這樣手動調用锰镀,store.dispatch
方法會觸發(fā) Reducer 的自動執(zhí)行娘侍。為此,Store 需要知道 Reducer 函數(shù)泳炉,做法就是在生成 Store 的時候憾筏,將 Reducer 傳入createStore
方法。
import { createStore } from 'redux'; const store = createStore(reducer);
上面代碼中花鹅,createStore
接受 Reducer 作為參數(shù)氧腰,生成一個新的 Store。以后每當store.dispatch
發(fā)送過來一個新的 Action翠胰,就會自動調用 Reducer容贝,得到新的 State。
為什么這個函數(shù)叫做 Reducer 呢之景?因為它可以作為數(shù)組的reduce
方法的參數(shù)斤富。請看下面的例子,一系列 Action 對象按照順序作為一個數(shù)組锻狗。
const actions = [ { type: 'ADD', payload: 0 }, { type: 'ADD', payload: 1 }, { type: 'ADD', payload: 2 } ]; const total = actions.reduce(reducer, 0); // 3
上面代碼中满力,數(shù)組actions
表示依次有三個 Action,分別是加0
轻纪、加1
和加2
油额。數(shù)組的reduce
方法接受 Reducer 函數(shù)作為參數(shù),就可以直接得到最終的狀態(tài)3
刻帚。
3.7 純函數(shù)
Reducer 函數(shù)最重要的特征是潦嘶,它是一個純函數(shù)。也就是說崇众,只要是同樣的輸入掂僵,必定得到同樣的輸出。
純函數(shù)是函數(shù)式編程的概念顷歌,必須遵守以下一些約束锰蓬。
- 不得改寫參數(shù)
- 不能調用系統(tǒng) I/O 的API
- 不能調用
Date.now()
或者Math.random()
等不純的方法,因為每次會得到不一樣的結果
由于 Reducer 是純函數(shù)眯漩,就可以保證同樣的State芹扭,必定得到同樣的 View。但也正因為這一點赦抖,Reducer 函數(shù)里面不能改變 State舱卡,必須返回一個全新的對象,請參考下面的寫法摹芙。
// State 是一個對象 function reducer(state, action) { return Object.assign({}, state, { thingToChange }); // 或者 return { ...state, ...newState }; } // State 是一個數(shù)組 function reducer(state, action) { return [...state, newItem]; }
最好把 State 對象設成只讀灼狰。你沒法改變它,要得到新的 State浮禾,唯一辦法就是生成一個新對象交胚。這樣的好處是,任何時候盈电,與某個 View 對應的 State 總是一個不變的對象蝴簇。
3.8 store.subscribe()
Store 允許使用store.subscribe
方法設置監(jiān)聽函數(shù),一旦 State 發(fā)生變化匆帚,就自動執(zhí)行這個函數(shù)熬词。
import { createStore } from 'redux'; const store = createStore(reducer); store.subscribe(listener);
顯然,只要把 View 的更新函數(shù)(對于 React 項目吸重,就是組件的render
方法或setState
方法)放入listen
互拾,就會實現(xiàn) View 的自動渲染。
store.subscribe
方法返回一個函數(shù)嚎幸,調用這個函數(shù)就可以解除監(jiān)聽颜矿。
let unsubscribe = store.subscribe(() => console.log(store.getState()) ); unsubscribe();
四、Store 的實現(xiàn)
上一節(jié)介紹了 Redux 涉及的基本概念嫉晶,可以發(fā)現(xiàn) Store 提供了三個方法骑疆。
- store.getState()
- store.dispatch()
- store.subscribe()
import { createStore } from 'redux'; let { subscribe, dispatch, getState } = createStore(reducer);
createStore
方法還可以接受第二個參數(shù),表示 State 的最初狀態(tài)替废。這通常是服務器給出的箍铭。
let store = createStore(todoApp, window.STATE_FROM_SERVER)
上面代碼中,window.STATE_FROM_SERVER
就是整個應用的狀態(tài)初始值椎镣。注意诈火,如果提供了這個參數(shù),它會覆蓋 Reducer 函數(shù)的默認初始值状答。
下面是createStore
方法的一個簡單實現(xiàn)冷守,可以了解一下 Store 是怎么生成的。
const createStore = (reducer) => { let state; let listeners = []; const getState = () => state; const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); }; const subscribe = (listener) => { listeners.push(listener); return () => { listeners = listeners.filter(l => l !== listener); } }; dispatch({}); return { getState, dispatch, subscribe }; };
五剪况、Reducer 的拆分
Reducer 函數(shù)負責生成 State教沾。由于整個應用只有一個 State 對象,包含所有數(shù)據(jù)译断,對于大型應用來說授翻,這個 State 必然十分龐大,導致 Reducer 函數(shù)也十分龐大孙咪。
請看下面的例子堪唐。
const chatReducer = (state = defaultState, action = {}) => { const { type, payload } = action; switch (type) { case ADD_CHAT: return Object.assign({}, state, { chatLog: state.chatLog.concat(payload) }); case CHANGE_STATUS: return Object.assign({}, state, { statusMessage: payload }); case CHANGE_USERNAME: return Object.assign({}, state, { userName: payload }); default: return state; } };
上面代碼中,三種 Action 分別改變 State 的三個屬性。
- ADD_CHAT:
chatLog
屬性- CHANGE_STATUS:
statusMessage
屬性- CHANGE_USERNAME:
userName
屬性
這三個屬性之間沒有聯(lián)系墓臭,這提示我們可以把 Reducer 函數(shù)拆分奔滑。不同的函數(shù)負責處理不同屬性,最終把它們合并成一個大的 Reducer 即可合陵。
const chatReducer = (state = defaultState, action = {}) => { return { chatLog: chatLog(state.chatLog, action), statusMessage: statusMessage(state.statusMessage, action), userName: userName(state.userName, action) } };
上面代碼中枢赔,Reducer 函數(shù)被拆成了三個小函數(shù),每一個負責生成對應的屬性拥知。
這樣一拆踏拜,Reducer 就易讀易寫多了。而且低剔,這種拆分與 React 應用的結構相吻合:一個 React 根組件由很多子組件構成速梗。這就是說,子組件與子 Reducer 完全可以對應襟齿。
Redux 提供了一個combineReducers
方法姻锁,用于 Reducer 的拆分。你只要定義各個子 Reducer 函數(shù)猜欺,然后用這個方法位隶,將它們合成一個大的 Reducer。
import { combineReducers } from 'redux'; const chatReducer = combineReducers({ chatLog, statusMessage, userName }) export default todoApp;
上面的代碼通過combineReducers
方法將三個子 Reducer 合并成一個大的函數(shù)替梨。
這種寫法有一個前提钓试,就是 State 的屬性名必須與子 Reducer 同名。如果不同名副瀑,就要采用下面的寫法弓熏。
const reducer = combineReducers({ a: doSomethingWithA, b: processB, c: c }) // 等同于 function reducer(state = {}, action) { return { a: doSomethingWithA(state.a, action), b: processB(state.b, action), c: c(state.c, action) } }
總之,combineReducers()
做的就是產生一個整體的 Reducer 函數(shù)糠睡。該函數(shù)根據(jù) State 的 key 去執(zhí)行相應的子 Reducer挽鞠,并將返回結果合并成一個大的 State 對象。
下面是combineReducer
的簡單實現(xiàn)狈孔。
const combineReducers = reducers => { return (state = {}, action) => { return Object.keys(reducers).reduce( (nextState, key) => { nextState[key] = reducers[key](state[key], action); return nextState; }, {} ); }; };
你可以把所有子 Reducer 放在一個文件里面信认,然后統(tǒng)一引入。
import { combineReducers } from 'redux' import * as reducers from './reducers' const reducer = combineReducers(reducers)
六. redux設計和使用的三項原則:
- store是唯一的
- 只有store能夠改變自己的內容:不是reducer更新的均抽,而是store拿到reducer返回的數(shù)據(jù)自己對自己更新
- reducer必須是純函數(shù):純函數(shù)指給定固定的輸入嫁赏,就一定有固定的輸出,而且不會有任何的副作用油挥,一旦函數(shù)里有setTimeout或者Ajax請求或者和日期相關的,不再是一個純函數(shù)攘乒,reducer里面不能有異步操作和時間相關的操作惋鹅。對參數(shù)的修改就是副作用
七. redux中核心的api:
- createStore:幫助創(chuàng)建store
- store.dispatch:store里面的方法则酝,幫助我們派發(fā)action沽讹,這個action會傳遞給store
- store.getState:幫助我們去獲取store里面所有的數(shù)據(jù)內容
- store.subscribe:可以讓我們訂閱store的改變般卑,只要store發(fā)生改變妥泉,store.subscribe這個函數(shù)接受到的這個回調函數(shù)就會被執(zhí)行
八. react中UI組件與容器組件的拆分:更容易維護
- UI組件(傻瓜組件):負責頁面的渲染,不負責邏輯
- 容器組件(聰明組件):負責頁面的邏輯刽沾,不負責UI排拷,只用ui組件而已监氢,負責整個組建的功能實現(xiàn)
九.中間件
9.1.
到底什么是Redux中間件:action和store之間浪腐,是dispatch這個方法泽谨,中間件是對dispatch的一個封裝吧雹,或者對dispatch方法的一個升級雄卷,比如:使用redux-thunk對dispatch做了一個升級丁鹉,根據(jù)參數(shù)不同做不同的事情悍抑。當調用dispatch方法搜骡,且傳遞的參數(shù)是一個對象的話记靡,dispatch會直接把這個對象傳給store。假設傳給dispatch的是一個函數(shù)嚎花,不會把這個函數(shù)直接傳遞給store,它會讓你這個函數(shù)先執(zhí)行道逗,執(zhí)行完需要調用store的時候再去調用store滓窍,還有很多中間件
- redux-thunk: 把異步操作放在action中去操作
- redux-logger:可以記錄action每一次派發(fā)的日志吏夯,對dispatch做了升級噪生,在每次傳遞給store之前(在派發(fā)每個action的時候)杠园,把action打印出來在控制臺
- redux-saga:解決react中異步問題的中間件陈醒。單獨把異步邏輯拆分出來放到另一個文件里進行管理
9.2.
使用Redux-thunk中間件進行ajax請求發(fā)送:遇到異步請求或者非常復雜的邏輯钉跷,移除到其他地方(actionCreators)進行統(tǒng)一管理,做自動化測試的時候會變得簡單膝晾,比測試生命周期函數(shù)簡單Redux-thunk這個中間件可以把異步請求或者復雜的邏輯放到action里去處理血当,把異步操作的代碼從組件里移除到action里面去落恼,使用redux-thunk后佳谦,action可以不是返回一個對象钻蔑,==而可以返回函數(shù)==,函數(shù)里可以做異步操作,還有要去調用這個函數(shù)钝满。把異步函數(shù)放在組建的生命周期函數(shù)里面弯蚜,這個函數(shù)會變得越來越復雜,組件變得越來越大收厨,
componentDidMount() {
const action = getTodoList();
store.dispatch(action);//當調用store.dispatch诵叁,把action發(fā)給store時,store發(fā)現(xiàn)這不是一個對象侥锦,action就會被自動執(zhí)行恭垦,action就是一個函數(shù)祸泪,這個函數(shù)會發(fā)一個請求
//對比
// axios.get('/list.json').then((res)=>{
// const data = res.data;
// const action = initListAction(data);
// store.dispatch(action);
// })
}
//actionCreators里面的
export const getTodoList = () => {
return (dispatch) => {
axios.get('/list.json').then((res)=>{//當你調用getTodoList生成一個內容是函數(shù)的這種action的時候,這個函數(shù)能接收到store的dispatch方法
const data = res.data;
const action = initListAction(data);//可以直接用這個方法
dispatch(action);//把action傳給store右蒲,store判斷action這是一個對象瑰妄,故直接接收這個對象,改變他原始的狀態(tài)
})
}
};
- 安裝:npm install redux-thunk
- 重啟:npm run start
- 安裝好后使用:創(chuàng)建store的時候使用這個中間件竹宋,引入applyMiddleware
- 引入redux-thunk模塊
- 創(chuàng)建store的時候,在store的第二個參數(shù)里填寫一個applyMiddleware(thunk))
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(
reducer,
applyMiddleware([thunk, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()])
);//創(chuàng)建store的時候使用reucer構建初始的數(shù)據(jù)飒硅,然后在創(chuàng)建store的時候store會使用一個中間件thunk。把thunk寫成數(shù)組的形式尽棕,__REDUX_DEVTOOLS_EXTENSION__也是一個redux的中間件
- 什么時候用==redux的中間件==:通過redux創(chuàng)建store的時候要使用
- 使用多個中間件滔悉,redux-devtools也是中間件,使得store既支持window下的devtools歉提,可以去調試store苔巨,又引入了redux-thunk
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk';
const composeEnhancers =window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk),
// other store enhancers if any
);
const store = createStore(reducer, enhancer);
export default store;
9.3.
Redux-saga中間件的使用:做異步代碼拆分的中間件
- 安裝:npm install --save redux-saga
- 使用:先引入方法createSagaMiddleware(幫助創(chuàng)建中間件) import createSagaMiddleware from 'redux-saga'
- 創(chuàng)建中間件:const sagaMiddleware = createSagaMiddleware()
- 如何用這個中間件:
const enhancer = composeEnhancers(
// applyMiddleware(thunk),
applyMiddleware(sagaMiddleware),
);
- 創(chuàng)建saga文件:在redux-saga里面,會把異步邏輯放在單獨的地方(sagas.ja)去管理
- 引入saga:import todoSagas from './sagas';
- 調用sagaMiddleware.run(todoSagas)方法柿扣,讓saga文件執(zhí)行起來
- saga里的內容:先只寫一個generetor函數(shù),saga這個文件寫法必須要求里面的函數(shù)是generator函數(shù)
function* mySaga() {
}
export default mySaga;
- 不只reducer可以接收到action司草,sagas.js文件里面也可以接收到action
- 在saga這個文件里面引入takeEvery這個方法:import { takeEvery } from 'redux-saga/effects';
- generator函數(shù)里面要使用yield這樣一個語法yield takeEvery("USER_FETCH_REQUESTED", fetchUser);takeEvery意思是去捕捉每一個,"USER_FETCH_REQUESTED"是action的類型,只要接收到一個某個類型的action的話峦树,就會執(zhí)行fetchUser這個方法,可以通過takeEvery去捕獲到每一次派發(fā)出來的action
function* mySaga() {
yield takeEvery(GET_INIT_LIST, getInitList);
}//在getInitList這個函數(shù)里面去發(fā)Ajax請求
- 代替store.dispatch(action);:put(action);要先引入put方法import { takeEvery, put } from 'redux-saga/effects';
- 在generator里面做異步請求不要使用promise這種形式
function* getInitList() {
const res = yield axios.get('/list.json');//yield會等待你Ajax數(shù)據(jù)獲取完畢谷遂,把結果直接存在res里面(取數(shù)據(jù))
const action = initListAction(res.data);//取完把數(shù)據(jù)結果再創(chuàng)建一個action派發(fā)給store
yield put(action);}//等action處理完成之后再繼續(xù)往下執(zhí)行代碼(派發(fā)給store)
//對Ajax的失敗做處理
try {
const res = yield axios.get('/list.json');
const action = initListAction(res.data);
yield put(action);//把異步代碼寫在try里面
}catch(e) {
console.log('list.json 網(wǎng)絡請求失敗');
}
- redux-saga比redux-thunk復雜得多,redux-saga里有非常多的api集晚,call, put, takeEvery, takeLatest ,但是處理大型項目時莲绰,單獨弄文件來管理復雜的業(yè)務邏輯會更好辞友,但是redux-thunk沒有什么api,讓我們在action里面返回的內容既可以是對象也可以是函數(shù)
十. React-Redux的使用:
React-Redux是第三方的模塊踏枣,幫助我們在react中更方便的使用redux,
- 安裝:npm add react-redux
- 重啟服務器:npm run start
- Provider是react-redux提供的第一個核心api马昨,Provider的意思是:我這個提供器連接了store,那么我這個Provider里面所有的組件都有能力獲取到store里面的內容匙奴,
//index.js里面的內容
import React from 'react';
import ReactDOM from 'react-dom';
// import App from './App';
import TodoList2 from './TodoList2';
import { Provider } from 'react-redux';//Provider是一個組件
import store from './store1';
const App = (
<Provider store={store}>
<TodoList2 />
</Provider>
)//jsx
ReactDOM.render(App, document.getElementById('root'));//把APP作為組件傳給,整個頁面要渲染的是app這個組件
- connect是react-redux提供的第二個核心api哗伯,子組件通過connect獲取到store里的數(shù)據(jù),connect函數(shù)接收三個參數(shù)虐块,最后一個參數(shù)是組件非凌,代表這個組件要和store做連接颁糟,前兩個參數(shù)是連接的規(guī)則。首先要把store里的數(shù)據(jù)和組件里的數(shù)據(jù)的關系在mapStateToProps里理清楚婚脱,其次把組件里面props如何對store里的數(shù)據(jù)做修改和store里的dispatch方法做關聯(lián),通過mapDispatchToProps這個函數(shù)返回的對象來做關聯(lián)
//TodoList.js里面
import React, { Component } from 'react';
// import store from './store1';
import { connect } from 'react-redux';//引入的是react-redux之中的對象的某一部分篮洁,要做解構賦值
class TodoList2 extends Component {
// constructor(props) {
// super(props);
// this.state = store.getState();
// }
render() {
return (
<div>
<div>
<input value={this.props.inputValue} onChange={this.props.changeInputValue}/>
<button>提交</button>
</div>
<ul>
<li>Dell</li>
</ul>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
inputValue: state.inputValue
}
}//mapStateToProps把store里面的數(shù)據(jù)映射給這個組件,變成組建的props,state是store里的數(shù)據(jù)
//在和store做連接的時候要有一定的連接方式枷颊。函數(shù)固定寫法會接受一個state(store里的數(shù)據(jù))偷卧,return出對象坐求。讓TodoList和store做連接须妻,連接有一個映射關系荒吏,store里面的公用數(shù)據(jù)會映射到組建的props里面瞧挤,store里面的inputValue|會映射到組件里面的props的inputValue里面
//第二個參數(shù)特恬,函數(shù)癌刽,接收的是dispatch方法,返回對象.
//想對store的數(shù)據(jù)做修改远荠,也可以通過store的props來做修改矮台,Dispatch是發(fā)布,指的是store.dispatch确虱,把store.dispatch掛載(映射到)到props上,故可以通過this.props.什么東西去調用store.dispatch方法
const mapDispatchToProps = (dispatch) => {
return {
changeInputValue(e) {
const action = {
type: 'change_input_value',
value: e.target.value
}
dispatch(action);//改變store里的數(shù)據(jù)就要調用dispatch方法去派發(fā)action給store
}//這個函數(shù)能調用store.dispatch方法,參數(shù)dispatch就是store.dispatch方法
}
}//想派發(fā)action的時候,把這樣的操作放在mapDispatchToProps里面去宜咒,它可以讓props里面的方法能夠調用dispatch去操作store里的數(shù)據(jù)
export default connect(null, null)(TodoList);//導出connect方法,把TodoList傳給這個方法场晶。讓我的TodoList組件和store進行連接,做鏈接就有規(guī)則扳炬,規(guī)則在mapStateToProps里面
////只需用connect方法就可以==自動==幫助我們把這個組件結合這兩個規(guī)則和store做連接
//導出connect方法執(zhí)行的結果
- React-Redux的使用:
const { inputValue, list, changeInputValue, handleClick } = this.props; //解構賦值侦高,定義一個inputValue變量,=它的值等于this.props.inputValue
//導出去的是connect方法執(zhí)行的結果瞧壮,導出去的是一個容器組件
export default connect(mapStateToProps, mapDispatchToProps)(TodoList2);//只需用connect方法就可以自動幫助我們把這個組件結合這兩個規(guī)則和store做連接
connect方法把這些映射關系和業(yè)務邏輯集成到TodoList這個ui組件之中,TodoList是一個UI組件,當用connect把這個UI組件和一些數(shù)據(jù)和業(yè)務邏輯相結合的時候灯谣,返回的內容(connect方法執(zhí)行生成的內容的結果)實際是一個容器組件,容器組件對UI組件進行包裝辜窑,去調用這個UI組件,在調用時把數(shù)據(jù)和方法都準備好了
十一. 使用combineReducers完成對數(shù)據(jù)的拆分管理:
- 搜索GitHub上面的redux-devtools-exten惨远,點擊進入redux-devtools-extension插件葡幸,compose這個函數(shù)在redux這個庫里面存在床蜘,compose實際是一個包裝函數(shù)扬蕊,可以向這個函數(shù)里面?zhèn)鬟f很多方法尾抑,傳遞的方法會依次執(zhí)行,想用redux去調試就要裝redux-devtools-extension插件。這個工具有時光穿梭機的功能抗悍,之前的操作可以幫助你復現(xiàn),
- 不能讓reducer變得太大疟暖,redux提供一個新方法田柔,把一個手冊拆分成很多小手冊進行管理俐巴,按分類去拆分。
- store下的reducer要整合小的reducer硬爆,把他聚合成一個大的筆記本
- redux提供的函數(shù)combineReducers,它可以把一些小的reducer合并成大的reducer欣舵,調用這個方法合并缀磕。combineReducers會把小的reducer做一個整合然后生成一個最終的reducer出來缘圈。
- 把跟header相關的數(shù)據(jù)和操作放到了headerReducer里面去,在header下創(chuàng)建一個store文件夾袜蚕,再建一個reducer.js存放的是header相關的默認數(shù)據(jù)和對數(shù)據(jù)的操作糟把,然后拿到headerRedux對他做一個整合,整合出一個大的reducer牲剃,
- store下面的index.js是header的store部分的一個出口文件遣疯,可以通過引用store下面的index.js來間接引入reducer,
import { combineReducers } from 'redux';
import headerReducer from '../common/header/store/reducer';
export default combineReducers({
header: headerReducer
})
//其他寫法
import { combineReducers } from 'redux';
import headerReducer from '../common/header/store/reducer';
// import headerReducer from '../common/header/store/reducer';
//會自動到store下面找index.js,index.js恰好道出了reducer凿傅,可以直接引入reducer缠犀,減少目錄結構
//reducer as headerReducer起別名,as是壓縮的語法
import { reducer as headerReducer } from '../common/header/store';
// export default combineReducers({
// header: headerReducer
// })
const reducer = combineReducers({
header: headerReducer
});
//導出整合后的大的reducer
export default reducer;