我的react状态管理方式 createConveyor

254 阅读4分钟

在知乎提问,当React中需要的context是真单例,有方式不使用Context.Provider吗? - 知乎 (zhihu.com)

然后在大佬的回答帮助下,参考zustand的写法,进一步化简:

const createConveyor = value => {  
    const updaters = new Set();  
    const setValue = newVal => {    
        if (!Object.is(newVal, value)) {      
            value = newVal;      
            updaters.forEach(updater => updater());    
        }  
    }  
    return () => {    
        const [, forceUpdate] = useReducer(x => x + 1, 0);    
        useEffect(() => {      
            updaters.add(forceUpdate);      
            return () => updaters.delete(forceUpdate);            
        }, []);    
        return [value, useCallback(setValue, [])];  
    }
}

// example
export const useCounter = createConveryor(0)
function A () {
    const [count, setCount] = useCounter() //拥有取值、修改和订阅更新的能力
    // ...
}

conveyor是传送器的意思,取这个名字感觉很贴切

注:如果是大对象,那就老老实实用redux吧,毕竟是很重要的东西,一点点用reducer拆开写,可复用性要好一点才行。useContext?不好,大对象的值变了,所有消费组件会一起更新。

ok,完成case的收敛

注:recoil还有很多其他的状态管理库,大而全。但现在觉得redux + 这个conveyor就很好啊。毕竟如果事情最后无法变得更简单,那一开始就不应该变复杂。

大对象和selector概念

发觉react就像是风流才子,才华横溢,却也不负责任。特别是状态管理,感觉是react最差劲的地方。为什么?因为react把更新依赖收集这个事情交给开发者去做了。看看多如牛毛的状态管理库,里面但凡出现类似selector概念api,其实就是在做依赖收集(只有当我订阅的某个属性变化我才更新,因为我不能跟着总会变的最高层context去更新)。react就像手动档汽车。开起来很上头,但是终归是麻烦那么一点点,这是事实。

所以还是不得不继续思考,如果共享的是一个大对象,到底怎么办才最好?不要说redux,redux变成这个样子,是为了数据可追溯,但凭良心,这个就是没用,没用就是没用,只要reducer写得好,出了问题的时候debug看代码快多了,redux-log没啥用。突然不喜欢redux。前面说封装,我不按redux范式,一样可以把方法封装得好好的。另外,redux洁简是洁简,是为了面向中间件设计。反正中间件只要完成action to action的事情就好了。redux只要完成reducer to update的事情就好了。看起来很美好。用起来,挺难过。我们要的是个轮子,redux却给了我们一个正方形的轮子。

setState api

发现问题的本质应该是,从react把setState api交给开发者手上的那一天起,就注定了什么时候更新,由开发者决定。这里不是特指class组件,即使useState也是setState api。就好比是虽然你获得了绝世神兵,但拯救世界的重任也落到你肩上了。凭良心说,react的更新机制是如此的透明洁简,换来的代价就是,你不得不考虑何时去收集和触发组件的更新。

终章 immer.js

妈妈问我为什么跪着看代码,是immer.js

之前我知道react应避免突变式改值,这是因为react很多地方的更新是靠引用对比

但我不知道immer.js是怎么做的,但现在知道了

终于我能放心的修改大对象了

我的redux reducer不用再分case了,不再写任何逻辑分类,只怼一个大对象进去

reducer应当降级为底层api

用中间件派生action写逻辑分类

做action to action的中间件saga和redux-observable 突然变帅了

(涉及异步,用中间件写派生action跑不掉,本质是为了在回调中获取最新状态做状态合并;如果不用处理异步的中间件,你就要拆很多个同步的action,这是更不能接受的,对于多层对象来说你不可能每种操作就拆一个,因为我与其在这里拆分,不如在异步之前就拆?用中间件写的异步action才有复用副作用的意义啊。不然你拆同步action,是拆了个寂寞,这是我不喜欢把逻辑分类写到reducer的原因)

最后,局部共享对象,好像还是可以用上面的createConveyor吧

react-create-conveyor - npm (npmjs.com)