formily/ractive-react

615 阅读2分钟

核心概念

Observable

创建可订阅的对象,每次操作该对象属性的过程中会通知订阅者,使用proxy来创建

@formily/reactive中通过以下几个API来创建observable对象:

  • observable函数创建深度的observable对象:
    1. observable.deep 创建深劫持observable对象
    2. observab.shallow 创建浅劫持observable对象
    3. observab.computed 创建缓存计算器
    4. observable.box 创建带get/set方法的observable对象
    5. observable.ref 创建引用级的observable对象
  • define函数定义observable领域模型,可以组合observable函数与其其静态属性函数完成领域模型的定义
  • model函数定义自动observable领域模型,他会将getter和setter属性包装为computed属性,将函数包为action,其他数据属性用observable(深劫持)包装

reaction

在响应式中它就是可订阅对象的订阅者,接收tracker函数,函数执行时如果函数内部对observable对象中的某个属性进行读操作,那么reaction就会与该属性进行一个绑定。一旦该属性在其他地方进行写操作就会重复执行tracker函数

创建reaction的API:

  • autorun 创建一个自动执行的响应器
  • reaction 创建一个可以实现脏检查的响应器
  • Tracker 创建一个依赖追踪器,需用户手动执行追踪

测试用例

import React from 'react'

const obj = {
    count: 1
}

const ReactivePage = () => {
    const countAdd = () => {
        obj.count++
        console.log('count:',obj.count);
    }
    return (
        <>
            <h1>ReactivePage</h1>
            <button onClick={countAdd}>{obj.count}</button>
        </>
    )
}

export default ReactivePage

点击button,log(count)一直加但是组件没更新 如何控制组件更新?

  • 用observable(reactive)包裹obj
  • 用observer(reactive-react)包裹ReactivePage
const obj = observable({
    count: 1
})

export default observer(ReactivePage)

实现

hook

目的:让组件更新

import { useEffect, useReducer, useRef } from "react";
import { Tracker } from "../../../which";

export function useObserver(view) {
  const [_, forceUpdate] = useReducer((i) => i + 1, 1);
  //因为自定义hook会随着函数组件的更新多次执行,防止会多次实例化,将实例存起来,所以用useRef(存在对应组件的fiber上)
  const trackerRef = useRef(null);
  if (!trackerRef.current) {
    //可变化对象的属性值发生变化,组件跟着更新。
    //自己获取reaction
    trackerRef.current = new Tracker(() => {
      forceUpdate();
    });
  }

  //组件卸载前取消订阅,防止内存泄露。
  useEffect(() => {
    return () => {
        if(trackerRef.current){
            trackerRef.current.dispose()
        }
    }
  },[])

  //创建组件的追踪器
  return trackerRef.current.tracker(view)
}

observer

import { useObserver } from "./hook";
import { memo } from "react";

export default function observer(component) {
  const WrappedComponent = (props) => {
    return useObserver(() => component({ ...props }));
  };

  const memoComponent = memo(WrappedComponent);

  return memoComponent;
}