Skip to content

State

引言

当创建好AutoStore实例后就可以存取状态。

  • 使用useReactive

用来在组件中访问和更新Store的状态数据,更新时会导致重新渲染。

  • 直接读写store.state

store.state返回的是一个响应式对象reactive,其实质是通过Proxy实现的。 当读写store.state时,会触发内部的依赖收集,相关计算属性的运行,配合signal机制可以自动触发组件的细粒度重新渲染。

useReactive

Store对象提供了useReactive方法,用来在组件中访问和更新Store的状态数据。

函数签名

ts
export interface UseReactiveType<State extends Dict> {
    <Path extends StatePaths<State> = StatePaths<State>>(selector: Path): UseReactiveResult<GetTypeByPath<ComputedState<State>,Path> ,State>
    <Path extends StatePaths<State> = StatePaths<State>>(selector: Path,async:boolean): UseReactiveResult<AsyncComputedValue<GetTypeByPath<State,Path>> ,State>
    <Value=any>(selector: string[]): UseReactiveResult<Value,State>
    <Value=any>(selector: ObjectKeyPaths<ComputedState<State>>,async:boolean): UseReactiveResult<AsyncComputedValue<Value>,State>
    <Value=any,SetValue=any>(getter: UseReactiveGetter<Value,State>,setter?:UseReactiveSetter<SetValue,State>): UseStateComposeResult<Value,SetValue,State>
    (): UseReactiveResult<State,State>
}

基础使用

其使用方式与ReactuseState方法类似,返回一个statesetState的元组。

tsx
import { 
createStore
} from "@autostorejs/react"
const {
state
,
useReactive
,
$
} =
createStore
({
user
:{
firstName
:"Zhang",
lastName
:"Fisher",
age
:18,
} }) // 使用方式1 : 能age自动推断类型 const [
age
,
setAge
] =
useReactive
('user.age')
// 使用方式2 不能自动推断类型 const [
firstName
,
setFirstName
] =
useReactive
(['user','firstName'])

简单示例如下:

WARNING

  • 当更新Age时会重新渲染整个组件,其行为与ReactuseState类似。
  • 在早期版本中,useReactiveuseState,其API设计尽可能保持与ReactuseState一致,减少用户的学习成本。但是很快发现useStateReact内置名称冲突,导致需要进行重命名,所以后续版本中更名为useReactive

getter & setter

useReactive还可以接受gettersetter两个函数参数,用来获取和设置State中的组合属性。

  • getter:用来获取State中的组合属性。
  • setter:用来设置State中的组合属性。
tsx
 
const { useReactive,state,$ } = createStore( {
  firstName:"Zhang",
  lastName:"Fisher",
  fullName:(state)=>state.firstName+state.lastName,
})

const [fullName,setFullName] = useReactive<string,[string,string]>(
    (state)=>state.fullName,       // getter
    ([first,last],state)=>{        // setter
      state.firstName=first
      state.lastName=last
    }
)

// 修改firstName和lastName
setFullName(["Hello","Voerkai18n❤️"]) 

提示

  • useReactive<string,[string,string]>可以指定gettersetter的泛型类型,这样在使用时会有更好的代码提示。
  • useReactive(<path string>)能自动类型推断,但是gettersetter需要手动指定泛型类型。

简单示例如下:

提示

useReactive还有一个别名useState,但是由于useStateReact内置名称相同,使用时经常需要重命名,所以在AutoStore中使用useReactive来代替。

useAsyncReactive

如果状态是一个异步计算属性,也可以使用useAsyncReactive来处理。

useAsyncReactive返回是一个AsyncComputedValue对象,其包含了异步计算属性的状态信息。

更多异步计算的特性见异步计算属性

直接读写

除了使用useReactive方法读写状态外,sotre.state返回的是一个响应式Proxy对象,可以直接读写也会触发内部的依赖收集和事件响应。

ts
const { state , $ } = createStore({
  age:18
})

// 直接更新Age也会触发响应和依赖收集,但是不会触发重新渲染整个组件
state.age=100

特殊注意:

  • 直接修改状态state.age=100会触发内部的依赖收集和事件响应,但是不会触发重新渲染整个组件。因为state是一个响应式对象(即经过proxy处理过的对象),对其的读写会触发内部的依赖收集和事件响应。但是并没有通知React组件,所以不会触发组件的重新渲染。
  • 如果要让直接读写状态时触发重新渲染,就需要对state进行包装,让状态变化时通知React组件重新渲染。这需要使用:
    • 使用useReactive
    • 使用signal信号组件

无论是使用useReactive信号组件均原理均是会拦截state读写事件,然后通知React组件重新渲染。

简单演示如下:

WARNING

此例中更新Age时并不会重新渲染整个组件,而只会渲染$('age'),这就是信号组件的功能,其可以提供细粒度的更新渲染。 $('age')本质上返回的是一个经过React.memo包装的ReactNode

静默更新

对状态进行读取时,会触发相应的StateOperateType类型的事件,如getset等。

在某些场景下,我们可能不希望触发这些事件,可以使用silentUpdate方法。

tsx
store.silentUpdate(()=>{
  store.state.age=100
})

批量更新

一般情况下,更新多个状态时会触发多个更新事件。在React场景中,为了优化渲染,我们可能希望一次性更新多个状态,只触发一次渲染。此时就可以使用batchUpdate方法。

tsx
store.batchUpdate(()=>{
  store.state.age=100
  store.state.name="Fisher"
})

关于更多的批量更新的技术细节见批量更新

静默读取

正常访问状态时会触发get事件,如果不希望触发get事件,可以使用peep方法。

tsx
store.peep((state)=>{
  return state.age
})
// 读取age不会触发get事件
store.peep("age") // 100

以上方法不会触发get <age>事件。

update

update方法用来更新状态,其函数签名如下:

tsx
type UpdateOptions = {
    batch?:boolean | string,         
    silent?:boolean,        
    peep?:boolean           
    reply?:boolean
}
update(fn:(state:ComputedState<State>)=>void,options?:UpdateOptions)
  • batchUpdate仅是update((state)=>{....},{batch:true})的快捷方式。
  • silentUpdate仅是update((state)=>{....},{silent:true})的快捷方式。

小结

  • 更新Store的状态可以不需要使用useReactive返回的setXXXXX,直接使用state.xxxx=xxx即可更新状态触发响应。
  • 如果要提供细粒度的更新,可以使用signal机制,通过$方法创建一个信号组件,用来触发局部更新。