Redux是JavaScript的狀態(tài)容器般眉,它提供了可預(yù)測(cè)化的狀態(tài)管理。
Redux的核心理念:
-
Store
Store是存儲(chǔ)著數(shù)據(jù)的地方潜支,Redux應(yīng)用只有一個(gè)Store甸赃。
Redux提供createStore這個(gè)函數(shù),用來(lái)生成Store冗酿。
import {createStore} from 'redux'
////store(創(chuàng)建的時(shí)候需要傳入一個(gè)reducer)
const store = createStore(reducer);
function reducer(state,action){}
-
Action
所有數(shù)據(jù)的變化埠对,必須通過(guò)派發(fā)(dispatch)action來(lái)更新;
action是一個(gè)普通的JavaScript對(duì)象已烤,用來(lái)描述這次更新的type和content鸠窗;
const action1 = {
type: "ADD_FRIEND",
info: {
name: "boge",
age: 20
}
}
const action2 = {
type:"CHANGE_NAME",
playload: {
index: 0,
newName: "bogezhenshuai"
}
}
-
Reducer
Reducer是一個(gè)純函數(shù);
Reducer做的事情就是將傳入的state和action結(jié)合起來(lái)生成一個(gè)新的state胯究;
//定義initialState是為了給state一個(gè)默認(rèn)值
const initialState = {counter: 0}
function reducer(state = initialState,action){
switch (action.type){
case "INCREMENT":
return {...state,counter:state.counter + 1}
default:
return state;
}
}
Redux的三大原則:
1.單一數(shù)據(jù)源
- 整個(gè)應(yīng)用程序的state被存儲(chǔ)在一顆object tree中,并且這個(gè)object tree只存儲(chǔ)在一個(gè) store 中躁绸;
- Redux并沒(méi)有強(qiáng)制讓我們不能創(chuàng)建多個(gè)Store裕循,但是那樣做并不利于數(shù)據(jù)的維護(hù)臣嚣;
- 單一的數(shù)據(jù)源可以讓整個(gè)應(yīng)用程序的state變得方便維護(hù)、追蹤剥哑、修改硅则;
2.State是只讀的
- 唯一修改State的方法一定是觸發(fā)action,不要試圖在其他地方通過(guò)任何的方式來(lái)修改State株婴;
- 這樣就確保了View或網(wǎng)絡(luò)請(qǐng)求都不能直接修改state怎虫,它們只能通過(guò)action來(lái)描述自己想要如何修改state;
- 這樣可以保證所有的修改都被集中化處理困介,并且按照嚴(yán)格的順序來(lái)執(zhí)行大审,所以不需要擔(dān)心race condition(竟態(tài))的問(wèn)題;
3.使用純函數(shù)來(lái)執(zhí)行修改
- 通過(guò)reducer將 舊state和 actions聯(lián)系在一起座哩,并且返回一個(gè)新的State徒扶;
- 隨著應(yīng)用程序的復(fù)雜度增加,我們可以將reducer拆分成多個(gè)小的reducers根穷,分別操作不同state tree的一部分姜骡;
- 但是所有的reducer都應(yīng)該是純函數(shù),不能產(chǎn)生任何的副作用屿良;
Redux的使用過(guò)程
- 創(chuàng)建一個(gè)對(duì)象圈澈,作為我們要保存的狀態(tài):
- 創(chuàng)建Store來(lái)存儲(chǔ)這個(gè)state:
創(chuàng)建store時(shí)必須創(chuàng)建reducer;
我們可以通過(guò)store.getState來(lái)獲取當(dāng)前的state尘惧;
- 創(chuàng)建Store來(lái)存儲(chǔ)這個(gè)state:
- 通過(guò)action來(lái)修改state:
通過(guò)dispatch來(lái)派發(fā)action士败;
通常actoin中都會(huì)有type屬性,也可以攜帶其他的數(shù)據(jù)褥伴;
- 通過(guò)action來(lái)修改state:
- 修改reducer中的處理代碼
- 可以在派發(fā)action之前谅将,監(jiān)聽(tīng)store的變化;
import {createStore} from 'redux'
//定義initialState是為了給state一個(gè)默認(rèn)值
const initialState = {counter: 0}
//action
const action = {type:"INCREMENT"};
function reducer(state = initialState,action){
switch (action.type){
case "INCREMENT":
return {...state,counter:state.counter + 1}
}
}
//store(創(chuàng)建的時(shí)候需要傳入一個(gè)reducer)
const store = createStore(reducer);
//訂閱store的修改
store.subscribe(() => {
console.log("counter:",store.getState().counter); //顯示counter:1
});
//派發(fā)action
store.dispatch(action);
react-redux使用
安裝react-redux----yarn add react-redux
- 組件home.js文件
import React, { PureComponent } from 'react';
// import {connect} from '../utils/connect';
import {addAction,incAction,changeBannersAction,changeRecommendAction} from '../store/actionCreators';
import {connect} from 'react-redux';
class Home extends PureComponent {
render() {
return (
<div>
<h1>Home</h1>
<h2>當(dāng)前計(jì)數(shù):{this.props.counter}</h2>
<button onClick={e => this.props.increment()}>+1</button>
<button onClick={e => this.props.addNumber(5)}>+5</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
counter: state.counter
}
};
const mapDispatchToProps = dispatch => {
return{
increment: function(){
dispatch(incAction());
},
addNumber: function(num){
dispatch(addAction(num));
}
}
} ;
export default connect(mapStateToProps,mapDispatchToProps)(Home);
- index.js文件
import {Provider} from 'react-redux';
import store from './store';
ReactDOM.render{
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
};
Redux中的異步操作
Redux中進(jìn)行異步操作需要用到中間件重慢。
官網(wǎng)推薦網(wǎng)絡(luò)請(qǐng)求的中間件是使用:redux-thunk
redux-thunk是如何做到讓我們可以發(fā)送異步的請(qǐng)求呢饥臂?
- 在默認(rèn)情況下,在dispatch(action)中似踱,action通常是一個(gè)JavaScript的對(duì)象隅熙;
- redux-thunk可以讓dispatch(action函數(shù)),action可以是一個(gè)函數(shù)核芽;
- 該函數(shù)會(huì)被調(diào)用囚戚,同時(shí)傳給這個(gè)函數(shù)一個(gè)dispatch函數(shù)和getState函數(shù);
1.dispatch函數(shù)用于我們之后再次派發(fā)action轧简;
2.getState函數(shù)考慮到我們之后的一些操作需要依賴原來(lái)的狀態(tài)驰坊,用于讓我們可以獲取之前的一些狀態(tài);
使用redux-thunk
1. 安裝redux-thunk
yarn add redux-thunk
2. 創(chuàng)建store時(shí)傳入應(yīng)用middleware的enhance函數(shù)
通過(guò)applyMiddleware來(lái)結(jié)合多個(gè)Middleware哮独,返回一個(gè)enhancer拳芙;
將enhancer作為第二個(gè)參數(shù)傳入到createStore中察藐;
- ./store/index.js文件
import {createStore,applyMiddleware} from 'redux';
import thunkMiddleware from 'redux-thunk';
const enhancer = applyMiddleware(thunkMiddleware);
const store = createStore(reducer,enhancer);
3. 定義返回一個(gè)函數(shù)的action
注意:這里不是返回一個(gè)對(duì)象,而是一個(gè)函數(shù)舟扎;
該函數(shù)在dispatch之后被執(zhí)行分飞;
- ./store/actionCreators.js文件
const getHomeMultidataAction = () => {
return (dispatch) => {
axios.get("http://127.207.32.32:8000/home/multidata").then(res => {
const data = res.data.data;
dispatch(changeBannersAction(data.banner.list));
dispatch(changeRecommendsAction(data.recommend.list));
})
}
}
使用redux-saga
redux-saga是另一個(gè)比較常用在redux發(fā)送異步請(qǐng)求的中間件,它的使用更加的靈活睹限。
1. 安裝redux-saga
yarn add redux-saga
2. 集成redux-saga中間件
導(dǎo)入創(chuàng)建中間件的函數(shù)譬猫;
通過(guò)創(chuàng)建中間件的函數(shù),創(chuàng)建中間件羡疗,并且放到applyMiddleware函數(shù)中染服;
啟動(dòng)中間件的監(jiān)聽(tīng)過(guò)程,并且傳入要監(jiān)聽(tīng)的saga顺囊;
import createSageMiddleware from 'redux-saga';
import {createStore,applyMiddleware} from 'redux';
const sagaMiddleware = createSageMiddleware();
const storeEnhancer = applyMiddleware(thunkMiddleware,sagaMiddleware);
const store = createStore(reducer,enhancer);
//注意:若要運(yùn)行redux-saga中間件肌索,如下代碼
sagaMiddleware.run(saga);
3. saga.js文件的編寫
import {takeEvery,put} from 'redux-saga/effects';
import axios from 'axios';
import {
FETCH_HOME_MULTIDATA
} from './home/constants';
import { changeBannersAction, changeRecommendAction } from './home/actionCreators';
function *fetchHomeMultidata(action) {
const res = yield axios.get("http://123.207.32.32:8000/home/multidata");
const banners = res.data.data.banner.list;
const recommend = res.data.data.recommend.list;
yield put(changeBannersAction(banners));
yield put (changeRecommendAction(recommend));
}
function *mySaga(){
//takeLatest takeEvery區(qū)別
//takeLatest:依次只能監(jiān)聽(tīng)一個(gè)對(duì)應(yīng)的action
//takeEvery:每一個(gè)都會(huì)被執(zhí)行
yield takeEvery(FETCH_HOME_MULTIDATA,fetchHomeMultidata);
}
export default mySaga;
redux-saga常用API
方法 | 解釋 |
---|---|
takeEvery | 可以傳入多個(gè)監(jiān)聽(tīng)的actionType,每一個(gè)都可以被執(zhí)行 |
takeLatest | 依次只能監(jiān)聽(tīng)一個(gè)action |
put | 在saga中派發(fā)action不再通過(guò)dispatch特碳,而是用過(guò)put |
all | 可以在yield的時(shí)候put多個(gè)action |