react如何实现响应式数据更新hook

770 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

Reflect

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与Proxy() 的方法相同。Reflect不是一个函数对象,因此它是不可构造的。

简单使用

const target = {};
// 添加属性 
Reflect.set(target, "abc","123");
// 获取值
console.log(Reflect.get(target,"abc"));
Reflect.get(target, propertyKey[, receiver]);
Reflect.set(target, propertyKey, value[, receiver]);

而set和get都有设置this指向的值

receiver则为调用时的this值

const obj = {
    a: 2,
    get b() {
        return this.a;
    },
};
const obj2 = {
    a: 10,
};

console.log(Reflect.get(obj, "b", obj2)); // 10

对象的操作方式,都有对应的api,reflect使用请参考Reflect - JavaScript | MDN

// 普通写法
'assign' in Object // true

// Reflect写法
Reflect.has(Object, 'assign') // true

// 普通写法
Function.prototype.apply.call(Math.floor, undefined, [1.88]) // 1

// Reflect写法
Reflect.apply(Math.floor, undefined, [1.88]) // 1

// 普通写法
delete myObj.foo;

// Reflect写法
Reflect.deleteProperty(myObj, 'foo');

// new 的写法
const instance = new Greeting('张四');

// Reflect.construct 的写法
const instance = Reflect.construct(Greeting, ['礼物']);

// 普通写法
Object.defineProperty(obj, 'now', {
  value: () => Date.now()
});

// Reflect写法
Reflect.defineProperty(obj, 'now', {
  value: () => Date.now()
});

Proxy实现响应式

// 原对象:代理过的对象
const proxyMap = new WeakMap();
// 代理过的对象:原对象
const rawMap = new WeakMap();

function observer(obj, cb) {
  const existingProxy = proxyMap.get(obj);
  // 添加缓存 防止重新构建proxy
  if (existingProxy) {
    return existingProxy;
  }
  // 已经代理过
  if (rawMap.has(obj)) {
    return obj;
  }

  const proxy = new Proxy(obj, {
    set(target, p, value, receiver) {
      const ret = Reflect.set(target, p, value, receiver);
      cb();
      return ret;
    },
    get(target, p, receiver) {
      const res = Reflect.get(target, p, receiver);
      // res当为对象时代理它
      return res && typeof res === "object" ? observer(res, cb) : res;
    },
    deleteProperty(target, p) {
      const ret = Reflect.deleteProperty(target, p);
      cb();
      return ret;
    },
  });

  proxyMap.set(obj, proxy);
  rawMap.set(proxy, obj);

  return proxy;
}

例子

const init = {
    _name: "abc",
    get name() {
        return this._name;
    },
    a: {
        c: [1, 2, 35, 6],
    },
};

const ret = observer(init, () => {
// 当值改变时触发
    console.log(ret);
    console.log(init);
    document.body.innerHTML = ret.name;
});

以上是数据响应式的实现,当对对象深层赋值时在get判断获取到的值是否为对象,若是设置为代理,若否着直接返回值,而数据的变化一般都是赋值、获取值、删除值。

使用WeakMap是避免内存泄露,它的key必须是对象,并且是弱引用,当key给设置为null,给垃圾回收时,当前的在WeakMap设置也会跟着给回收

封装为react的响应式数据hook

const useReactive = (initState) => {
  const [, update] = useState({});
  const stateRef = useRef(initState);
  const isInit = useRef(true);

  if (isInit.current) {
    isInit.current = false;
    stateRef.current = observer(initState, () => update({}));
  }

  return stateRef.current;
};

这里使用ref进行数据的储存,当数据改变的强制渲染组件以达到数据的更新

而一般需要这种场景的是既要数据实时,又要数据改变触发渲染,以此界面展示达到预期