如果你不知道是否需要 Redux,那就是不需要它。简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。《redux入门教程 - 阮一峰》
前言
我们回想下我们为什么准备用redux,是否我们的交互已经复杂到难以维护,是否存在复杂的共享状态,是否存在复杂的组件间交互,如果并没有以上的复杂度,我们也许并非需要redux。我个人也在业务中用过redux,但是我的业务大多是些轻交互的H5页面,redux沦为一种强规范。另外请相信我,有的程序员特别擅长把强规范写成一团糟。
useReducer
Hook是 React 16.8 的新增特性。它可以让我们在不编写 class 的情况下使用 state 以及其他的 React 特性。其中useReducer引起了我的注意,满满的亲切感铺面。回到前言,我的业务逻辑实际上并没有那么复杂,但是我还想利用redux强规范的特性来把视图和逻辑做很好的归置,useReducer显然很适合。
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
如上,我可以把逻辑组织为reducr函数,Counter组件只负责视图层的展示,从而实现了很好的视图逻辑分离。
问题
但是我们的业务不只是前端的自娱自乐,我们还需要做诸如打日志、请求接口等含副作用的操作。这时候单纯的useReducer就无能为力了,因为他只能处理一些同步的操作。我们开始怀念redux的中间件(ps:原来我只是馋redux的中间件)。
useReducer + middleware = useEnhancer
既然我们馋的是中间件,那我就简单粗暴把它们结合起来。先看下基础使用
import { React, FC useReducer } from 'react';
import useEnhancer from '@ivliu/use-enhancer';
const fakeThunk = () => next => async action => {
if(typeof action === 'function') {
action = await action();
}
await next(action);
}
const fakeLog = () => next => async () => {
console.log('sorry, i am a fake log.');
await next();
}
const reducer = (state, action) {
const { type, payload } = action;
switch(type) {
case 'ASYNC_TYPE':
return { ...state, ...payload };
default:
return state;
}
}
const App: FC = () => {
const [state, rawDispatch] = useReducer(reducer, {});
const dispatch = useEnhancer(
state,
rawDispatch,
fakeThunk,
fakeLog,
);
useEffect(() => {
dispatch(async () => {
await new Promise(r => setTimeout(() => r(), 3000));
return ({ type: 'ASYNC_TYPE', payload: { /* some data */ }})
})
}, [])
return (<h3 onClick={() => dispatch({ type: 'SOME_TYPE', payload: {} })}>{ /* state */}</h3>)
};
看到这里大家应该明白我为什么起名字叫enhancer,他其实就是一个基于中间件的dispatch增强器,我们需要什么能力都可以写个中间件实现,比如我内置的thunk插件,我们可以利用它非常方便的实现异步操作。
派生Action
有些时候,我们的操作是为了执行下一个或多个操作,这听起来比较拗口。举个例子,我们的接口请求操作实际没有什么意义,根据接口成功或失败执行的对应的操作才有意义,我把这种根据某种操作产生的另一种操作成为派生action,它会被挂载到内部的action链表尾部,等所有派生action收集完毕后统一执行。
中间件
export type TMiddlewareWithoutAction = <R extends ReducerWithoutAction<any>>(
store: ReducerStateWithoutAction<R>,
dispatch: DispatchWithoutAction
) => (next: TNext) => () => Promise<void>;
export type TMiddleware = <R extends Reducer<any, any>>(
store: ReducerState<R>,
Dispatch: Dispatch<ReducerState<R>>
) => (next: TNext) => (action: ReducerAction<R>) => Promise<void>;
export type TNext = <R extends Reducer<any, any>>(action?: ReducerAction<R>) => Promise<void>;
以上是中间件的类型定义,我们可以在闭包内部访问到当前store,dispatch,next,以及action。
双向链表
type TLink = { callback: TLinkCallback; pending: Promise<any> | null; prev: TLink; next: TLink;} | null;
其中中间件通过双向链表的形式组织,可以充分利用小块内存。
后话
我觉得我文笔一般,这也是我第一次在掘金发帖,希望大家可以不吝建议。
github地址 github.com/IVLIU/use-e…