react自定义hooks之【useReactive】

730 阅读2分钟

用过vue的人都知道我们改变data里面的数据,直接改变原属性值就可以达到页面更新视图的效果,那么我们在react里面也实现一个类似的hook。

useReactive

提供一种数据响应式的操作体验,定义数据状态不需要写useState,直接修改属性即可刷新视图。

思路

  • 创建函数

  •   export const useReactive = <S extends Record<string, any>>(initialState: S): S => {
        return initialState;
      };
      
    
  • 通过proxy代理数据劫持

    • 创建observer(观察者函数)

    •   export const useReactive = <S extends Record<string, any>>(initialState: S): S => {
          function observer<T extends Record<string, any>>(initialVal: T, cb: () => void): T {
            return initialVal
          }
          return initialState;
        };
      
    • 创建proxy对象

    •   export const useReactive = <S extends Record<string, any>>(initialState: S): S => {
          function observer<T extends Record<string, any>>(initialVal: T): T {
            const proxy = new Proxy(initialVal, {
              get(target: T, p: string | symbol, receiver: any): any {
                const res = Reflect.get(target, p, receiver);
                return res;
              },
              set(target: T, p: string | symbol, receiver: any): boolean {
                const res = Reflect.get(target, p, receiver);
                return res;
              },
              defineProperty(target: T, p: string | symbol, attributes: PropertyDescriptor): boolean {
                const res = Reflect.defineProperty(target, p, attributes);
                return res;
              },
            });
            return proxy;
          }
          const state = observer(initialState)
          return state;
        };
      
  • 测试函数,哈哈哈其实这个时候还没写完,还有好多bug

  • 我们先让hook能正常使用

  •   export const useReactive = <S extends Record<string, any>>(initialState: S): S => {
        const [, setState] = useState({});
        function observer<T extends Record<string, any>>(initialVal: T, cb: () => void): T {
          const proxy = new Proxy(initialVal, {
            get(target: T, key: string | symbol, receiver: any): any {
              const res = Reflect.get(target, key, receiver);
              return isObject(res) ? observer(res, cb) : Reflect.get(target, key, receiver);
            },
            set(target: T, key: any, receiver: any): boolean {
              const ret = Reflect.set(target, key, receiver);
              cb();
              return ret;
            },
            defineProperty(target: T, key: string | symbol, attributes: PropertyDescriptor): boolean {
              const ret = Reflect.defineProperty(target, key, attributes);
              cb();
              return ret;
            },
          });
          return proxy;
        }
      
        const stateRef = useRef<S>(initialState);
      
        const state = observer(stateRef.current, () => {
          // 重新render 保证页面刷新
          setState({});
        });
        return state;
      };
      
      
    
  • 至此我们的useReactive可以正常的运行了

  • 试试对象

  • 都能正常运行


优化

  • 我们现在每次更改数据都会创建一个proxy,利用闭包缓存。

  •   export const useReactive = <S extends Record<string, any>>(initialState: S): S => {
        // 更新视图
        const [, setState] = useState({});
        // 代理对象
        const proxyMap = new WeakMap();
        function observer<T extends Record<string, any>>(initialVal: T, cb: () => void): T {
          const cacheProxy = proxyMap.get(initialVal);
          if (cacheProxy) return cacheProxy;
          const proxy = new Proxy(initialVal, {
            get(target: T, key: string | symbol, receiver: any): any {
              const res = Reflect.get(target, key, receiver);
              return isObject(res) ? observer(res, cb) : Reflect.get(target, key, receiver);
            },
            set(target: T, key: any, receiver: any): boolean {
              const ret = Reflect.set(target, key, receiver);
              cb();
              return ret;
            },
            defineProperty(target: T, key: string | symbol, attributes: PropertyDescriptor): boolean {
              const ret = Reflect.defineProperty(target, key, attributes);
              cb();
              return ret;
            },
          });
          proxyMap.set(initialVal, proxy);
          return proxy;
        }
        const stateRef = useRef<S>(initialState);
        return observer(stateRef.current, () => {
          // 重新render 保证页面刷新
          setState({});
        });
      };
    
  • 完结撒花!!!