- Redux 解决了复杂应用的状态管理问题,可以跨层级任意传递数据
组件直接与store通信
如果项目中应用了多层级嵌套的一些组件,需要跨层级传递数据的场景很多,那么利用props的方式传数据将会很麻烦,需要父子组件一层层传,数据传来传去还可能容易产生bug,不易维护,那么使用Redux来管理状态就是为了解决了这个问题
- Redux是一个经典的发布-订阅器
// 发布订阅的一个例子
Button.addEventListener('click', ()=>{}) 订阅 当触发A的时候 就执行B
Button.removeEventListener('click',()=>{}) 取消订阅 当触发A的时候 不再执行B
Button.onClick //发布 - 触发A
- Redux和react-redux是两个不同的库,react-redux将redux和react连接起来
- redux仅支持同步数据流,可用中间件thunk实现异步数据流
文件夹结构
Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。
Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。当前时刻的 State,可以通过store.getState()拿到。Redux 规定, 一个 State 对应一个 View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什么样,反之亦然。
action是一个对象,用字符串变量类型的type字段表示action的名称, 是store数据的唯一来源,Action 就是 View 发出的通知,表示 State 应该要发生变化了。
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函数接受另一个函数作为参数,返回新生成的 Store 对象。()=>({type: XXX})
export const decrement = createAction(DECREMENT_COUNT);
export const increment = createAction(INCREMENT_COUNT);
actionType demo.ts
当应用规模越来越大时,建议使用单独的模块或文件来存放 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 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。由于 Reducer 是纯函数,就可以保证同样的State,必定得到同样的 View。但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象。
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 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。
这种写法有一个前提,就是 State 的属性名必须与子 Reducer 同名。
总之,combineReducers()做的就是产生一个整体的 Reducer 函数。该函数根据 State 的 key 去执行相应的子 Reducer,并将返回结果合并成一个大的 State 对象。
reducer index.tx
import {combineReducers} from 'redux';
import demo from './demo';
const rootReducer = combineReducers({demo});
export default rootReducer;
index.ts
//redux默认的dispatch默认接收一个object类型的action,所以action不能是异步,引入中间件Redux-thunk可以来解决异步问题
//中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。
import {createStore, applyMiddleware, compose} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers/index';
//compose会将传入的函数数组从右向左串联执行
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(applyMiddleware(thunkMiddleware));
//applyMiddleware用于调用各种中间件
export default createStore (
rootReducer,
enhancer
)
//store.dispatch方法会触发 Reducer 的自动执行,为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。
// 上面代码中,createStore接受 Reducer 作为参数,生成一个新的 Store。以后每当store.dispatch发送过来一个新的 Action,就会自动调用 Reducer,得到新的 State。
// 调用createStore方法创建一个store对象,可以接收三个参数,reducer,preloadedState(在服务端渲染时用),enhancer(中间件),
//store是保存数据的地方,一个项目只有一个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把数据往下传
//react有三种数据:context:向子组件,孙组件传递数据,props:父组件向子组件传递数据,state:组件自身数据
function App() {
return (
<Provider store ={store}>
<Counter />
</Provider>
);
}
//Provider包裹跟组件 传入store对象
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 发出 Action 的唯一方法。接受一个 Action 对象作为参数,将它发送出去。
},
reduceData(data:any):void {
dispatch(decrement())
}
})
export default connect(mapStateToPorps, mapDispatchToProps)(Counter);
//组件引入 connect方法,将需要更新的组件连接起来,并传入mapStateToProps,mapDispatchToProps
//connect是一个高阶组件,接收Provider传递过来的store对象,订阅store中的数据,如果store中的数据发生变化,调用setState触发组件更新
首先,用户发出 Action。
然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State 。
State 一旦有变化,Store 就会调用监听函数。通过store.getState()得到当前状态。如果使用的是 React,这时可以触发重新渲染 View。
定义state,dispatch的映射。
然后,使用connect方法生成容器组件。
然后,定义这个组件的 Reducer。
最后,生成store对象,并使用Provider在根组件外面包一层。