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
constdescriptors=(obj)=>{returnObject.keys(obj).reduce((handles,key)=>{letvalue=obj[key];// 如果 value 是个对象,那就递归其属性进行 `setter/getter`if(Object.prototype.toString.call(obj)==="[object Object]"){value=Object.defineProperty({},descriptors(value));}return{
...handles,[key]: {get(){returnvalue},set(newVal){setState({ ...state,[key]: newVal})}}}},{})}
如果你仔细观察了这段代码,会发现有个非常致命的问题。那就是在做递归的时候,set(newVal) 里面的代码并不对,state 是个深层对象,不能这么简单地对其外层进行赋值。
这意味着,我们需要将访问这个对象深层属性的一整条路径保存下来,以便于 set 到正确的值,可以用一个数组来收集路径上的 key 值。
这里用使用 lodash 的 set 和 get 来做一下演示。
1. 前言
前几天在知乎看到了一个问题,React 的 Hooks 是否可以改为用类似 vue3 composition api 的方式实现?
关于 React Hooks 和 Vue3 Composition API 的热烈讨论一直都存在,虽然两者本质上都是实现状态逻辑复用,但在实现上却代表了两个社区的不同发展方向。
我想说,小孩子才分好坏,成年人表示我全都要。
2. 你不知道的 Object.defineProperty
那今天我们来讨论一下怎么用 React Hooks 来实现 Vue3 Composition 的效果。
先来看一下我们最终要实现的效果。
看到这个 API 的用法你会联想到什么?没错,很明显这里借用了 Proxy 或者
Object.defineProperty
。在《你不知道的 Proxy:ES6 Proxy 能做哪些有意思的事情?》一文中,我们已经对比过两者的用法了。
其实这里还有一个不为人知的区别,那就是可以通过
Object.defineProperty
给对象添加一个新属性。打印出来的效果是这样的:
这就很有意思了,意味着我们可以把某个对象 A 上所有属性都挂载到对象 B 上,这样我们不必对 A 进行任何监听,即不会污染 A。
3. React Hooks + Object.defineProperty = ?
如果将上面的代码结合 React Hooks,那会出现什么效果呢?没错,我们的 Hooks 变得更加
reactive
了。将这段代码进一步封装,可以得到一个 Custom Hook,也就是我们今天要说的 Composition API。
当然,这段代码还存在很多问题,依赖了对象的结构、不支持更深层的
getter/setter
等等,我们接下来就一起来优化一下。4. 实现 Composition
4.1 递归劫持属性
对于有多个属性的对象来说,我们可以遍历,配合
Object.defineProperties
来劫持它的所有属性。而对于更深层的对象来说,不仅要做递归,还要考虑 setState 这里应该根据访问路径来设置。
首先,我们来对深层对象做一次递归。
如果你仔细观察了这段代码,会发现有个非常致命的问题。那就是在做递归的时候,
set(newVal)
里面的代码并不对,state
是个深层对象,不能这么简单地对其外层进行赋值。这意味着,我们需要将访问这个对象深层属性的一整条路径保存下来,以便于
set
到正确的值,可以用一个数组来收集路径上的 key 值。这里用使用 lodash 的 set 和 get 来做一下演示。
但是,如果传入的是个数组,这里就会有问题了。因为我们只是对 Object 进行了拦截,没有对 Array 进行处理。
5. 完整版
这样,我们就实现了一个完整版的
ref
,我将代码和示例都放到了codesandbox
上面:Compostion API❤️ 看完三件事
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
The text was updated successfully, but these errors were encountered: