最近看了尤雨溪关于 vue3 的一些分享视频,也去 MDN 看了 Proxy 的内容。我们知道 vue3 是根据 Proxy 实现响应式编程的,那么能不能通过 Proxy 也让 react 实现响应式编程呢
如何实现数据与 Dom 的对应,我还是利用 react 自带的 setState ,很简单地,只需要修改 Proxy 的 set 方法
import {useState} from "react"
const useObserve = <T extends object>(initState: T) => {
const [observeState, setObserveState] = useState<T>(initState)
return new Proxy<T>(observeState, {
set(target: T, p: string | symbol, value: any, receiver: any): boolean {
const result: boolean = Reflect.set(target, p, value, receiver)
if (result) {
setObserveState({...target})
}
return result
}
})
}
export default useObserve
这是一个基础版本,如果对象是 {a: 3, b: "str"} 这样的简单对象(非嵌套对象),那我们的 hook 还是能生效的,但针对 {a: {b: 3}} 这样的嵌套对象就不行了,可能是嵌套的深层对象并不是 proxy 对象,如果打印对象,可以看到深层的对象并没有 Proxy 的标志,下图中的 {c: 5} 正是 Proxy 对象的 b 参数
那么知道了原因,就好办了,get 的时候判断是不是对象,如果是对象,那么就再用 Proxy 包一层就够了
import { useState } from 'react';
type Target = Record<string | symbol, any>;
const useObserve = <T extends object>(initState: T) => {
const [observeState, setObserveState] = useState<T>(initState)
const validator = {
set(target: Target, p: string | symbol, value: any, receiver: any): boolean {
const result: boolean = Reflect.set(target, p, value, receiver)
if (result) {
setObserveState(prevObserveState => ({ ...prevObserveState }))
}
return result
},
get(target: Target, p: string | symbol, receiver: any): any {
const value = Reflect.get(target, p, receiver)
if (typeof value === 'object' && value !== null) {
return new Proxy(value, validator)
}
return value
}
}
return new Proxy<T>(observeState, validator)
}
export default useObserve;
这里把 validator 独立出来,方便对所有的对象进行重复操作,setObserveState(prevObserveState => ({ ...prevObserveState })) 是关键步骤,使用解构获取最新的对象来实现数据的更新
目前为止,这个 hook 还是很粗糙的,即使测试的高度嵌套的对象都能符合预期,个人即使在项目中还是老老实实的用 react 的机制。如果你想在 react 中使用响应式方式进行编程,有 mobx 、 @vue/reactivity 这种经过大量测试和实践检测的库。希望这篇文章对你有帮助,也欢迎各位在评论区交流