Skip to content

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

如有转载或 CV 的请标注本站原文地址