You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constloggerBefore=(action,state)=>{console.log('logger before dispatch action:',{ action, state });};constloggerAfter=(action,state)=>{console.log('logger after dispatch action:',{ action, state });};
这篇文章主要讲两部分内容, 两个内容完全无关, 第一个是如何在
useReducer
中增加简单的middleware
机制, 第二个是如何实现compose
函数useReducer 与 middleware
完整代码: https://stackblitz.com/edit/react-gjsy9j
实现的效果如下:
useReducer
本身是不支持middleware
的, 不过可以实现一个自定义 hook, 在 reducer 计算的过程中(状态发生变更之前和之后), 增加可插入式的middleware
.还是以最基本的 todolist 为例子, 首先定义基本状态:
定义对应的
reducer
:App
定义如下:使用的
middleware
也定义一下, 这里就简单定一个一个logger
函数, 用于打印即将(之前之后)会触发的action
, 以及当前所对应的状态:由于
useReducer
并不支持第三个 middleware 参数, 因此需要自己实现一个useReducerWithMiddleware
的 custom hook, 需要注意的有以下几点:先考虑最基础的在
dispatch
一个action
之前的 middleware本质的实现其实就是用一个函数多包装一层, 接受相同的 action 参数, 里面在调用
dispatch(action)
时, 先调用一遍 middleware而基于上面定义的
logger
函数效果其实类似在定义的 reducer 之前增加一个console.log
语句:具体使用该 hook 的时候如下:
接下来考虑
dispatch
一个action
之后的 middleware假设做如下实现:
很可惜该实现存在问题, 由于更新是异步的, 这种情况下拿到的 state 仍旧是之前的, 因此需要使用
useEffect
做状态变更的更新监听:仍旧存在一个问题在于, 无法获取到 action, 解决办法也很简单, 通过
ref
或者临时变量在dispatch
对应的action
的时候进行赋值, 即可拿到对应的action
, 代码如下:需要注意的是需要对
actionRef
做判断, 以及最后要将actionRef
清空, 因为严格意义上来讲可能存在其他action
也能触发状态的更新, 而我们需要的是仅针对一个 action 做一组之前/之后的 middleware 的调用.compose
redux 里有一个
compose
函数, 用来将多个函数组合调用, 比如我要调用compose(f2, f2, f1)(10)
其实就等同于f3(f2(f1(10)))
, 注意最先计算的是从最右边开始的, 举个例子:redux 官网对这个实现也是非常精致, 去掉 ts 类型如下:
通过
reduce
每次提取两个函数, 返回(...args) => a(b(...args))
这样包装后的函数体那如果不用
reduce
, 使用普通的 for 循环实现呢? 其实本质是一样的, 我当时实现的版本如下:区别仅在于需要做一次判断, 因为
reduce
一次拿两个函数, 普通的for
循环一次只拿一个然而测试的时候发现报错:
显示错误原因貌似出现了无限递归, 当时无法理解后去 stackoverflow 提问了一下, 其实造成错误的缘由很简单, 先看下面的简单的例子:
原因在于
a = () => 2
这个函数从来就没被调用过, 他只是被声明了一次, 而后面这个引用又重新被篡改了, 过程如下:a = () => 2
这个函数() => 3 * a()
, 而a()
从没被执行过a()
造成了无限递归函数在被声明和调用的过程中内部变量等可能是不一样的, 要做好区分
回到之前的实现, 其实也是类似的原因,
result
在每次循环中引用都被不断被改变,result
本身并没有在我想象的那样被"执行"拿到结果, 他仍旧是一个声明的函数体, 这就导致了最后出现自己调用自己造成无限递归改起来也简单, 用个变量接一下就好了:
这里的 r 每次都保留了当前循环环境下的 result 引用, 保证引用不再被篡改. 有点类似 stale closure, 不过这次有点反过来...
当然了, 写法可以有很多种, 比如后面有回答里给了这种写法:
这里就不再细究了
参考
The text was updated successfully, but these errors were encountered: