- Redux 解決了復(fù)雜應(yīng)用的狀態(tài)管理問題,可以跨層級(jí)任意傳遞數(shù)據(jù)
組件直接與store通信
如果項(xiàng)目中應(yīng)用了多層級(jí)嵌套的一些組件,需要跨層級(jí)傳遞數(shù)據(jù)的場(chǎng)景很多,那么利用props的方式傳數(shù)據(jù)將會(huì)很麻煩,需要父子組件一層層傳,數(shù)據(jù)傳來傳去還可能容易產(chǎn)生bug,不易維護(hù),那么使用Redux來管理狀態(tài)就是為了解決了這個(gè)問題
- Redux是一個(gè)經(jīng)典的發(fā)布-訂閱器
// 發(fā)布訂閱的一個(gè)例子
Button.addEventListener('click', ()=>{}) 訂閱 當(dāng)觸發(fā)A的時(shí)候 就執(zhí)行B
Button.removeEventListener('click',()=>{}) 取消訂閱 當(dāng)觸發(fā)A的時(shí)候 不再執(zhí)行B
Button.onClick //發(fā)布 - 觸發(fā)A
- Redux和react-redux是兩個(gè)不同的庫,react-redux將redux和react連接起來
- redux僅支持同步數(shù)據(jù)流,可用中間件thunk實(shí)現(xiàn)異步數(shù)據(jù)流
文件夾結(jié)構(gòu)

Store 就是保存數(shù)據(jù)的地方,你可以把它看成一個(gè)容器。整個(gè)應(yīng)用只能有一個(gè) Store。
Store對(duì)象包含所有數(shù)據(jù)。如果想得到某個(gè)時(shí)點(diǎn)的數(shù)據(jù),就要對(duì) Store 生成快照。這種時(shí)點(diǎn)的數(shù)據(jù)集合,就叫做 State。當(dāng)前時(shí)刻的 State,可以通過store.getState()拿到。Redux 規(guī)定, 一個(gè) State 對(duì)應(yīng)一個(gè) View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什么樣,反之亦然。
action是一個(gè)對(duì)象,用字符串變量類型的type字段表示action的名稱, 是store數(shù)據(jù)的唯一來源,Action 就是 View 發(fā)出的通知,表示 State 應(yīng)該要發(fā)生變化了。
action demo.ts
import {createAction} from 'redux-actions';
import { DECREMENT_COUNT, INCREMENT_COUNT } from '../actionTypes/demo';
export interface IAction<T> {
type: string;
payload: T;
}
//createStore函數(shù)接受另一個(gè)函數(shù)作為參數(shù),返回新生成的 Store 對(duì)象。()=>({type: XXX})
export const decrement = createAction(DECREMENT_COUNT);
export const increment = createAction(INCREMENT_COUNT);
actionType demo.ts
當(dāng)應(yīng)用規(guī)模越來越大時(shí),建議使用單獨(dú)的模塊或文件來存放 action。
export const INCREMENT_COUNT = 'INCREMENT_COUNT';
export type INCREMENT_COUNT = typeof INCREMENT_COUNT;
export const DECREMENT_COUNT = 'DECREMENT_COUNT';
export type DECREMENT_COUNT = typeof DECREMENT_COUNT;
reducer demo.ts
Store 收到 Action 以后,必須給出一個(gè)新的 State,這樣 View 才會(huì)發(fā)生變化。這種 State 的計(jì)算過程就叫做 Reducer。
Reducer 是一個(gè)函數(shù),它接受 Action 和當(dāng)前 State 作為參數(shù),返回一個(gè)新的 State。
Reducer 函數(shù)最重要的特征是,它是一個(gè)純函數(shù)。也就是說,只要是同樣的輸入,必定得到同樣的輸出。由于 Reducer 是純函數(shù),就可以保證同樣的State,必定得到同樣的 View。但也正因?yàn)檫@一點(diǎn),Reducer 函數(shù)里面不能改變 State,必須返回一個(gè)全新的對(duì)象。
function reducer(state,action) {
return {...state,...newState}
}
import {handleActions} from 'redux-actions';
import { IAction } from '../actions/demo';
import { INCREMENT_COUNT, DECREMENT_COUNT } from '../actionTypes/demo';
export interface IDemoState {
val: number;
}
//state初始值
const createInitialState: () => IDemoState = () => ({
val: 0
})
const demo = handleActions({
[INCREMENT_COUNT](state: IDemoState, action: IAction<IDemoState>):IDemoState {
const {payload} = action;
return {
...state,
val: state.val + 1
}
},
[DECREMENT_COUNT](state: IDemoState, action: IAction<IDemoState>):IDemoState {
const {payload} = action;
return {
...state,
val: state.val - 1
}
}
}, createInitialState())
export default demo;
Redux 提供了一個(gè)combineReducers方法,用于 Reducer 的拆分。你只要定義各個(gè)子 Reducer 函數(shù),然后用這個(gè)方法,將它們合成一個(gè)大的 Reducer。
這種寫法有一個(gè)前提,就是 State 的屬性名必須與子 Reducer 同名。
總之,combineReducers()做的就是產(chǎn)生一個(gè)整體的 Reducer 函數(shù)。該函數(shù)根據(jù) State 的 key 去執(zhí)行相應(yīng)的子 Reducer,并將返回結(jié)果合并成一個(gè)大的 State 對(duì)象。
reducer index.tx
import {combineReducers} from 'redux';
import demo from './demo';
const rootReducer = combineReducers({demo});
export default rootReducer;
index.ts
//redux默認(rèn)的dispatch默認(rèn)接收一個(gè)object類型的action,所以action不能是異步,引入中間件Redux-thunk可以來解決異步問題
//中間件就是一個(gè)函數(shù),對(duì)store.dispatch方法進(jìn)行了改造,在發(fā)出 Action 和執(zhí)行 Reducer 這兩步之間,添加了其他功能。
import {createStore, applyMiddleware, compose} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers/index';
//compose會(huì)將傳入的函數(shù)數(shù)組從右向左串聯(lián)執(zhí)行
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(applyMiddleware(thunkMiddleware));
//applyMiddleware用于調(diào)用各種中間件
export default createStore (
rootReducer,
enhancer
)
//store.dispatch方法會(huì)觸發(fā) Reducer 的自動(dòng)執(zhí)行,為此,Store 需要知道 Reducer 函數(shù),做法就是在生成 Store 的時(shí)候,將 Reducer 傳入createStore方法。
// 上面代碼中,createStore接受 Reducer 作為參數(shù),生成一個(gè)新的 Store。以后每當(dāng)store.dispatch發(fā)送過來一個(gè)新的 Action,就會(huì)自動(dòng)調(diào)用 Reducer,得到新的 State。
// 調(diào)用createStore方法創(chuàng)建一個(gè)store對(duì)象,可以接收三個(gè)參數(shù),reducer,preloadedState(在服務(wù)端渲染時(shí)用),enhancer(中間件),
//store是保存數(shù)據(jù)的地方,一個(gè)項(xiàng)目只有一個(gè)store
App.tsx
import { Provider } from 'react-redux';
import store from './store/index';
import Counter from './components/counter';
import 'antd/dist/antd.css';
// Provider是通過context api把數(shù)據(jù)往下傳
//react有三種數(shù)據(jù):context:向子組件,孫組件傳遞數(shù)據(jù),props:父組件向子組件傳遞數(shù)據(jù),state:組件自身數(shù)據(jù)
function App() {
return (
<Provider store ={store}>
<Counter />
</Provider>
);
}
//Provider包裹跟組件 傳入store對(duì)象
export default App;
Counter.tsx
import React from "react";
import {connect} from "react-redux";
import {increment, decrement} from '../store/actions/demo';
import {Button} from 'antd';
interface IProps {
val: number;
addData: (data:any) => void;
reduceData: (data:any) => void;
}
const Counter = (props:IProps) => {
const {val,addData,reduceData} = props;
return (
<div>
<Button onClick={addData}>加一</Button>
<Button onClick={reduceData}>減一</Button>
{val}
</div>
);
}
//定義value到state的映射,以及addData reduceData到dispatch的映射。
//將state注入組件props里
const mapStateToPorps = (state: any) => ({
val: state.demo.val
})
//將action注入組件props里
const mapDispatchToProps = (dispatch: any) => ({
addData(data:any):void {
dispatch(increment())
//dispatch()是 View 發(fā)出 Action 的唯一方法。接受一個(gè) Action 對(duì)象作為參數(shù),將它發(fā)送出去。
},
reduceData(data:any):void {
dispatch(decrement())
}
})
export default connect(mapStateToPorps, mapDispatchToProps)(Counter);
//組件引入 connect方法,將需要更新的組件連接起來,并傳入mapStateToProps,mapDispatchToProps
//connect是一個(gè)高階組件,接收Provider傳遞過來的store對(duì)象,訂閱store中的數(shù)據(jù),如果store中的數(shù)據(jù)發(fā)生變化,調(diào)用setState觸發(fā)組件更新
首先,用戶發(fā)出 Action。
然后,Store 自動(dòng)調(diào)用 Reducer,并且傳入兩個(gè)參數(shù):當(dāng)前 State 和收到的 Action。 Reducer 會(huì)返回新的 State 。
State 一旦有變化,Store 就會(huì)調(diào)用監(jiān)聽函數(shù)。通過store.getState()得到當(dāng)前狀態(tài)。如果使用的是 React,這時(shí)可以觸發(fā)重新渲染 View。
定義state,dispatch的映射。
然后,使用connect方法生成容器組件。
然后,定義這個(gè)組件的 Reducer。
最后,生成store對(duì)象,并使用Provider在根組件外面包一層。