Skip to content

状态内监视

除了通过store.watch来监听状态外,还可以在状态内部声明watch函数来监听状态数据的变化。

工作原理

@autostorejs/react提供了watch函数,用来在state中声明一个监听对象,然后监听函数的返回值写入声明所在路径。

watch函数的基本特性如下:

  • 在状态中的任意位置,使用watch(...)来声明一个监听器对象。
  • createStore执行后,会扫描状态数据,如果发现一个值是watch(...),则会创建一个WatchObject对象,用来监听State中的数据变化。
  • 创建的WatchObject对象会保存在Store对象的watchObjects对象中
  • 当所监听的数据发生变化时,会调用WatchObject对象的getter函数,然后将返回结果写入到声明watch(...)的位置。

基本用法

watch函数签名如下:

ts
// 监听filter过滤后的
function watch<Value=any, DependValue=any>(
  getter:WatchGetter<Value,DependValue>,
  filter?:WatchDependFilter<DependValue>,
  options?:Omit<WatchOptions<Value>,'filter'>
):WatchDescriptorBuilder<Value>
// 监听全部
function watch<Value=any, DependValue=any>(
  getter:WatchGetter<Value,DependValue>,
  options?:Omit<WatchOptions<Value>,'filter'>
):WatchDescriptorBuilder<Value>

watch函数基本使用如下:

在以上例子中,我们使用watch实现了computed的类似功能:

  • watch函数的第一个参数是getter函数,负责在依赖变化时计算新值。getter函数的返回值会写入watch函数所在的位置。
  • watch函数的第二个参数是一个过滤函数,当状态变化时会调用此方法,如果返回true才会执行getter
  • initial属性用来配置watch函数所在位置的total的初始值。

监听函数

watchgetter参数只能是一个同步函数,签名如下:

typescript
type WatchGetter<Value=any,DependValue= any> = (
    // 传入发生变化的路径和值
    scope: {path:string[],value:DependValue},
    // 创建的watchObject对象
    watchObject : WatchObject<Value>
)=>Exclude<any,Promise<any>> | undefined

监听参数

watch支持以下参数

ts
interface WatchOptions<Value=any> extends ObserverOptions<Value>  { 
    async?  : false                        
    filter : WatchDependFilter<Value>     
}
  • initial: 用来指定一个默认值
  • id: 用来指定一个唯一的标识,同时作为创建的WatchObjectkey,可以通过store.watchObjects.get(<id>)来访问。

动态依赖

computed计算函数的依赖一般是确定的,而watch函数的依赖可以是动态的。这比较适合一些需要动态侦听的场景。

比如上例中,我们动态侦听orders[].count的变化来计算total。而computed函数的依赖是静态的,一旦声明就不会变化。

以下是表单validate检测的简单示例:

tsx
const store = createStore({
      a:{
          validate:true
      },
      b:{
          validate:true
      },            
      c:{
          validate:true,
          c1:{
              validate:true
          }
      },
      validate:watch<boolean,boolean>(({path,value},watchObj)=>{   
          if(typeof(value) === 'boolean'){
              const srcKey = path.join('.')
              if(value){
                  delete watchObj.cache[srcKey]
              }else{
                  watchObj.cache[srcKey] = value
              }
          }
          // 由于cache里面只记录validate=false的值,所以如果cache不为空则代表有字段的validate=false
          return Object.keys(watchObj.cache).length==0

      },
      (path)=>path[path.length-1]=='validate', // 只侦听validate字段的值变化
      {initial:true,id:"x"})
  })

说明:

  • 上例中,我们需要实现一个validate字段来表单整个表单的有效,当状态中任意一个对象中的validate字段都为false时,则validate=false,否则为true
  • 现在问题是validate可能是在一个复杂的嵌套对象中,并且可能是动态的。这时候,我们无法使用computed来进行计算,因为computed的依赖是静态的。
  • 此时就是使用watch函数的时候了,我们声明一个watch函数,用来监听所有路径中的path[path.length-1]=='validate'字段的变化即可。
  • 关于WatchObject的介绍,可以参考监听对象