背景
当我们写React的时候我们该考虑什么?对于一部分同学会花费大量精力去研读React特性的部分源码,在实践中应用起来,对他们来说心智负担=0。对于大部分api调用者来说,可能考虑更多的是如何组织数据并让数据按照预期地渲染在页面上。
React带来的hooks比较完美地解决了这个问题,在 变与不变 间提供了 useRef
和 useReducer
,我们着重理解下 useReducer,告诉reducer 需要做的动作,reducer会负责更新数据并渲染页面。
痛点
来看下一个reducer的demo:
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>
</>
);
}
reducer本身是个函数,在这里面处理各种case,最终目的是返回下一个状态数据。这个模式如果正常写下来其实是不适合业务代码堆砌的:
- 不适合逻辑分离和复用
- 不适合流式处理业务逻辑和逻辑组合
比如:初始化业务的时候需要做:获取用户 + 获取配置,过程中还有单独的业务:获取用户、获取配置。代码组织方面比较繁琐。
如果抛开React谈业务是不是下面这段代码更加合适:
class Service {
state = {
user: undefined,
config: undefined
}
fetchUser() {}
fetchConfig() {}
init() {
fetchUser();
fetchConfig();
}
}
useZelda 只是最简单地去向这个方向靠近,代码如下:
function useZelda(_class) {
const {current: entity} = useRef(typeof _class ? new _class() : _class);
const [reducer, dispatch] = useReducer(function (prevState, action) {
if (action.payload) {
return action.payload;
}
return prevState;
}, entity.state);
const proxy = new Proxy(entity, {
get (target, p, receiver) {
if (p === 'state') {
return new Proxy(target.state, {
get (target, p, receiver) {
return target[p];
},
set() {
return false;
}
});
}
const source = target[p];
if (typeof source === 'function') {
return function(...args) {
const result = source.apply(target, args);
if (result === undefined) {
return;
}
if (result && typeof result === 'object' && typeof result.then === 'function') {
return result;
}
entity.state = result;
dispatch({ payload: result });
}
}
},
set (target, p, value, receiver) {
console.log('set', target, p, value, receiver);
if (p === 'state') {
return false;
}
target[p] = value;
return true;
}
});
return proxy;
}
这段代码是一段雏形,它具备了上述的写法,如果往后续发展,将中间件思想、分支概念、快照概念等各种想法加入进来可能会变成一个庞大的库,我也在项目中使用了dva,也许这种思想发展成另一条路就是dva的处理数据的方式。
它是一个种子,是开放的,在日常公司业务代码中也扩展了这个库到非React应用中去:支付宝小程序。它是自由的,带给我的是想法和开放,所以我命名为Zelda,希望它的路是广阔的。