useReactive
作用
提供一种数据响应式的操作体验,定义数据状态不需要写 useState,直接修改属性即可刷新视图。
原理
通过 Proxy 进行数据劫持和修改,实现数据响应式。
源码
ts
import { useRef } from 'react'
import { isPlainObject } from 'lodash-es'
import useCreation from '../useCreation'
import useUpdate from '../useUpdate'
// k:v 原对象:代理过的对象
const proxyMap = new WeakMap()
// k:v 代理过的对象:原对象
const rawMap = new WeakMap()
function observer<T extends Record<string, any>>(initialVal: T, cb: () => void): T {
const existingProxy = proxyMap.get(initialVal)
// 添加缓存 防止重新构建proxy
if (existingProxy) {
return existingProxy
}
// 防止代理已经代理过的对象
// https://github.com/alibaba/hooks/issues/839
if (rawMap.has(initialVal)) {
return initialVal
}
const proxy = new Proxy<T>(initialVal, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
// https://github.com/alibaba/hooks/issues/1317
const descriptor = Reflect.getOwnPropertyDescriptor(target, key)
if (!descriptor?.configurable && !descriptor?.writable) {
return res
}
// Only proxy plain object or array,
// otherwise it will cause: https://github.com/alibaba/hooks/issues/2080
return isPlainObject(res) || Array.isArray(res) ? observer(res, cb) : res
},
set(target, key, val) {
const ret = Reflect.set(target, key, val)
cb()
return ret
},
deleteProperty(target, key) {
const ret = Reflect.deleteProperty(target, key)
cb()
return ret
},
})
proxyMap.set(initialVal, proxy)
rawMap.set(proxy, initialVal)
return proxy
}
function useReactive<S extends Record<string, any>>(initialState: S): S {
const update = useUpdate()
const stateRef = useRef<S>(initialState)
const state = useCreation(() => {
return observer(stateRef.current, () => {
update()
})
}, [])
return state
}
export default useReactive