Redux-CheatSheet
本文详细地阐述了
Redux 的设计理念与实践技巧,包含了其三大原则与简单的仿制、基础组件以及React 集成使用、基于Thunk, Promise, Sagas 三种不同的异步处理方式、Selector, Ducks 等其他常见的样式规范、中间件的实现原理与代码分析等。
Redux CheatSheet | Redux 设计理念与实践技巧清单
基础组件
实践工具
ducks
ducks-modular-redux 是对于
// widgets.js
// Actions
const LOAD = "my-app/widgets/LOAD";
const CREATE = "my-app/widgets/CREATE";
const UPDATE = "my-app/widgets/UPDATE";
const REMOVE = "my-app/widgets/REMOVE";
// Reducer
export default function reducer(state = {}, action = {}) {
switch (action.type) {
// do reducer stuff
default:
return state;
}
}
// Action Creators
export function loadWidgets() {
return { type: LOAD };
}
export function createWidget(widget) {
return { type: CREATE, widget };
}
export function updateWidget(widget) {
return { type: UPDATE, widget };
}
export function removeWidget(widget) {
return { type: REMOVE, widget };
}
// side effects, only as applicable
// e.g. thunks, epics, etc
export function getWidget() {
return dispatch =>
get("/widget").then(widget => dispatch(updateWidget(widget)));
}
A module…
MUST export default a function called reducer() MUST export its action creators as functions MUST have action types in the form npm-module-or-app/reducer/ACTION_TYPE MAY export its action types as UPPER_SNAKE_CASE, if an external reducer needs to listen for them, or if it is a published reusable library
在外部使用时,我们可以导出默认的
import { combineReducers } from "redux";
import * as reducers from "./ducks/index";
const rootReducer = combineReducers(reducers);
export default rootReducer;
在组件中,可以导出所有的
import * as widgetActions from "./ducks/widgets";
redux-actions
import { createActions, handleActions, combineActions } from "redux-actions";
const defaultState = { counter: 10 };
const { increment, decrement } = createActions({
INCREMENT: (amount = 1) => ({ amount }),
DECREMENT: (amount = 1) => ({ amount: -amount })
});
const reducer = handleActions(
{
INCREMENT: (state, action) => ({
counter: state.counter + action.payload
}),
DECREMENT: (state, action) => ({
counter: state.counter - action.payload
})
},
defaultState
);
const reducer = handleActions(
{
[combineActions(increment, decrement)](
state,
{
payload: { amount }
}
) {
return { ...state, counter: state.counter + amount };
}
},
defaultState
);
export default reducer;
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise-middleware';
import thunkMiddleware from 'redux-thunk';
import logger from 'redux-logger';
const reducer = (state = {}, action) => {
...
}
const store = createStore(reducer, {}, applyMiddleware(
thunkMiddleware,
promiseMiddleware(),
logger,
));
export default store;
const promiseAction = () => ({
type: "PROMISE",
payload: Promise.resolve()
});
Async Actions | 异步Action 处理
Thunk
Promise
redux-promise 会自动处理success
或者 error
。
// 创建简单的异步 Action
createAction("FETCH_THING", async id => {
const result = await somePromise;
return result.someValue;
});
// 与自定义的 WebAPI 协同使用
import { WebAPI } from "../utils/WebAPI";
export const getThing = createAction("GET_THING", WebAPI.getThing);
export const createThing = createAction("POST_THING", WebAPI.createThing);
export const updateThing = createAction("UPDATE_THING", WebAPI.updateThing);
export const deleteThing = createAction("DELETE_THING", WebAPI.deleteThing);
redux-promise-middleware 为我们提供了类似的异步处理功能,其能够接受某个
const promiseAction = () => ({
type: "PROMISE",
payload: Promise.resolve()
});
该工具同样可以与
const secondAction = data => ({
type: "TWO",
payload: data
});
const first = () => {
return dispatch => {
const response = dispatch({
type: "ONE",
payload: Promise.resolve()
});
response.then(data => {
dispatch(secondAction(data));
});
};
};
我们也可以自己实现
const { fetchThing } = bindActionCreators({ fetchThing }, dispatch);
// 这里即会在 Redux 中分发 Action,同样也会阻塞执行直至 Promise 处理完毕
await fetchThing().payload;
Sagas

与

参考 fe-boilerplate/redux 的示例,我们首先需要引入并且创建
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../sagas/sagas';
const sagaMiddleware = createSagaMiddleware();
// 引入中间件,并构建 Store 对象
...
sagaMiddleware.run(rootSaga);
这里的
// helloSaga 会在 sagaMiddleware.run 时即刻执行
export function* helloSaga() {
console.log("Hello Saga!");
}
// worker saga
export function* incrementAsync() {
yield call(delay, 1000);
// 继续分发事件
yield put({ type: "SAGA_INCREMENT" });
}
// watcher saga
export function* watchIncrementAsync() {
// 监听 Action,并执行关联操作
yield takeEvery("SAGA_INCREMENT_ASYNC", incrementAsync);
}
// root saga
export default function* rootSaga() {
yield [helloSaga(), watchIncrementAsync()];
}
如 yield call(fetch, '/user')
即 yield
了下面的对象,call
创建了一条描述结果的信息,然后,redux-saga
{
type: CALL,
function: fetch,
args: ['/user']
}
我们也可以并发执行多个任务:
const [users, repos] = yield[(call(fetch, "/users"), call(fetch, "/repos"))];
同样以常见的接口请求,与结果处理为例:
import { take, fork, call, put } from "redux-saga/effects";
// The worker: perform the requested task
function* fetchUrl(url) {
// 指示中间件调用 fetch 异步任务
const data = yield call(fetch, url);
// 指示中间件发起一个 action 到 Store
yield put({ type: "FETCH_SUCCESS", data });
}
// The watcher: watch actions and coordinate worker tasks
function* watchFetchRequests() {
while (true) {
// 指示中间件等待 Store 上指定的 action,即监听 action
const action = yield take("FETCH_REQUEST");
// 指示中间件以无阻塞调用方式执行 fetchUrl
yield fork(fetchUrl, action.url);
}
}
样式风格
ducks
redux-actions
import { createActions, handleActions, combineActions } from "redux-actions";
const defaultState = { counter: 10 };
const { increment, decrement } = createActions({
INCREMENT: (amount = 1) => ({ amount }),
DECREMENT: (amount = 1) => ({ amount: -amount })
});
const reducer = handleActions(
{
INCREMENT: (state, action) => ({
counter: state.counter + action.payload
}),
DECREMENT: (state, action) => ({
counter: state.counter - action.payload
})
},
defaultState
);
const reducer = handleActions(
{
[combineActions(increment, decrement)](
state,
{
payload: { amount }
}
) {
return { ...state, counter: state.counter + amount };
}
},
defaultState
);
export default reducer;
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise-middleware';
import thunkMiddleware from 'redux-thunk';
import logger from 'redux-logger';
const reducer = (state = {}, action) => {
...
}
const store = createStore(reducer, {}, applyMiddleware(
thunkMiddleware,
promiseMiddleware(),
logger,
));
export default store;
const promiseAction = () => ({
type: "PROMISE",
payload: Promise.resolve()
});
Middleware | 中间件
实践的思考
仅就笔者的个人实践而言,在
对于数据的获取
对于简单可重复的全局状态,譬如通用的接口返回的错误信息,可以使用全局的错误状态:
function reducer(state, { payload }) {
if (payload.error) {
return {
...state,
errorMessage: payload.error.message
};
}
}
如果是复杂接口响应的处理,譬如创建或者更新的表单,特别是还需要包含大量的非跨组件的
class Com extends Component {
async handleSubmit() {
try {
const result = await doMutation();
if (result.success) {
// 执行成功之后的界面操作
showSuccessMessage();
fetchThing();
closeModal();
} else {
// 执行失败之后的部分操作
doFallback();
}
} catch (e) {
showErrorMessage();
}
}
}
关键源代码
react-redux
在