✍️ 使用Redux进行状态管理,简单的梳理和记录一下自己对Redux的工作流的理解以及使用
📌一、store
( ——state为单一数据源,用来存储全局共享的数据。)
使用:
// 获取数据
store.getState().___
// 更新组件
store.subscribe( () => {......} ) // 监听更新后触发,再次获取新的state
store.unsubscribe() // 卸载组件以解除状态监听
📌二、action
(——集中管理所有要做的行为,用来描述将要触发的事件。它只描述了一个行为)
使用:
// 一、action.js 定义
export const addConst = (payload) => {
type: "ADD_CONST",
payload: payload,
}
export const minusConst = (payload) => {
type: "SUB_CONST",
payload: payload,
}
// 二、组件中导入对应定义好的 action 使用
import { add } from 'action'
// 通过 store 的 dispatch() 分发 action
store.dispatch( add(___) )
// 三、接着进入 state ,并带当前的state和 action 进入 reducer 执行对应的函数更改状态
💡注意点:
- 实际上完全可以不进行action环节,组件可以直接
store.dispatch执行reducer,进行更新state状态,但是这样代码会比较混乱,导致异步操作和任何动作全部写在组件内,不易于追踪维护; (要写一堆杂七杂八东西在组件当中 😵) - 将所有要做的事情以函数形式并返回一个对象,然后再进行执行reducer,这样组件只需要调用action即可,异步操作和副作用都可以放到action当中中;
- 在Redux原生用法中action仅仅用于返回一个对象给
dispatch(......)函数参数使用;
📌三、reducer
(——通过触发的 action 事件,Store会自动来执行reducer并改变返回最新 state 值,描述如何改变数据)
// 在reducer.js 定义
const initialState = { count: 0 };
const reducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_CONST":
return { count: state.count + action.payload };
case "SUB_CONST":
return { count: state.count - action.payload };
default:
return state;
}
};
export default reducer;
💡注意点:
- reducer必须是一个纯函数,不能有任何的副作用 (同 Vuex中的 mutations);
- reducer仅仅只进行store.state状态的直接更新,这样才能追踪且可预测到状态的更新; (才能够热重载和时间旅行)
- 可以对reducer进行拆分,最后再并到一个rootReducer中执行组合;
📌四、与Vuex的比较
- Redux的
store.dispatch(......)类似于Vuex中的 ⇒ 组件的this.$store.dispatch(...)+ Action的context.commit(...)(其实更像commit,只不过可以通过函数返回对象参数的形式类似this.$store.dispatch的效果); - 在Vuex中,组件可以直接进行
commit()提交一个mutations修改state, 或者通过dispatch(...)的context.commit(...)提交mutations修改state; (作用上看Vuex的actions更像redux中间件处理异步行为) - 在Redux中通过
store.dispatch(......)触发并执行reducer函数, 不过通常大多数dispatch的参数经过由action处理并返回对象(进行所有的异步和副作用);
📌五、React-Redux
(——React官方库,增加更好的API帮助React使用Redux)
// 一、引入
import { Provider } from 'react-redux'
// 二、包裹根组件
<Provider store={store}>
<App />
</Provider>
// 三、在组件中使用 connect 高阶组件 (接收一个对象并返回一个高阶组件)
import { connect } from "react-redux";
const ReduxDemo: React.FC = (props) => {
return (
...
)
}
// 定义好需要映射的state
const mapStateToProps = (state) => {
return {
count: state.count,
};
};
// 定义好需要映射的despatch
const mapDispatchToProps = (dispatch) => {
return {
imcrement() {
dispatch(actionA())
}
};
};
export default connect(mapStateToProps)(ReduxDemo);
// 随后即可在组件 props 当中直接使用对应的 props.stateName 或者 props.actionName 使用
📌六、Redux-Thunk中间件
——可以让Redux的dispatch(......)方法执行函数,而不仅仅是对象,实现在action中执行dispatch进行异步操作,执行副作用,并把执行结果再dispatch到reducer中进行更改state状态,在组件只需要dispatch触发action即可
📌七、React-saga
——实现在组件dispatch后派发的action,会被saga拦截到,并在saga中进行异步操作后,在执行dispatch,给state的reducer更新状态;解决react-Thunk改变action的行为(action仅仅返回一个action对象,不应该执行其他操作,保持了其本质和纯度),而异步副作用等等则交给saga处理;
// 例子:
// store/index.js
import { rootSaga } from "./saga";
const sagaMiddleware = createSagaMiddleware();
export default createStore(reducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
// saga.js
import { takeEvery, put } from "redux-saga/effects";
import { changeAction } from "./action";
function* addFun(action) {
// action为被拦截的action
// 注意:拦截和saga再次派发的不能是同一个action,否则死循环
yield put(changeAction(action.value));
}
export function* rootSaga() {
// 监听拦截 对应dispatch的action的type
yield takeEvery(ADD_COUNT, addFun);
yield takeEvery(SUB_COUNT, subFun);
}
// components/myCom.js
import { addAction } from "../store/action";
// 注意:即使此被saga拦截但仍然会派发原来的action给到state的reducer处
dispatch(addAction(value)) //{ type: ADD_COUNT, value: payload }
📌八、使用dva
——轻量的React 应用级框架,集中整合React-Router + Redux + Redux-saga,其解决了store和saga过于分散,不便与开发,项目结构复杂的问题。
即:dva = React-Router + Redux + Redux-saga
Models.js
-
namespace:当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成 -
State:状态数据 -
Action:改变 State 的唯一途径,普通javascript对象,需在组件props.dispatch 函数派发;dispatch({ type: 'add', // required 用于指明具体的行为 payload: {}, ... }) -
Reducer:把之前的state和当前的状态聚合,返回的是一个新的累积结果,聚合积累的结果是当前 model 的 state 对象;// Action 处理器 处理同步动作 reducers: { add(state, { payload: id }) { return newState }, delete(state, { payload: id }) { return newState }, }, -
Effect:执行副作用,最常见的异步操作;Dva采用了generator的相关,所以将内部异步转成同步写法,从而将effects转为纯函数;// Action 处理器 处理异步动作 (注意:同redux-saga,依然会经过reducers更新store.state) effects: { // 内部是一个 Generator 函数,使用 yield 关键字,标识每一步的操作(不管是异步或同步) *add(action, { call, put }) { yield call(delay, 1000); yield put({ type: 'add' }); }, *sub() { ... } }, -
Subscription:用于提前定义好需要的订阅源数据subscriptions: { keyEvent({dispatch}) { key('ctrl+up', () => { dispatch({type:'add'}) }); },
API:
-
dva通常将容器组件约束为 Route Components(页面级别的组件)
-
connect方法(高阶组件):绑定 State 到 View,即让容器组件可以使用state,返回一个组件;(参数同react-redux) -
effect 函数内部常用处理函数:
- put:派发出一个 Action,类似于 dispatch
- call:执行异步函数,用于调用异步逻辑,支持 promise
- select:用于从 state 里获取数据
select(state => state.todos)