State
引言
当创建好AutoStore实例后就可以存取状态。
- 使用
useReactive
用来在组件中访问和更新Store的状态数据,更新时会导致重新渲染。
- 直接读写
store.state
store.state返回的是一个响应式对象reactive,其实质是通过Proxy实现的。
当读写store.state时,会触发内部的依赖收集,相关计算属性的运行,配合signal机制可以自动触发组件的细粒度重新渲染。
useReactive
Store对象提供了useReactive方法,用来在组件中访问和更新Store的状态数据。
函数签名
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>
}基础使用
其使用方式与React的useState方法类似,返回一个state和setState的元组。
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时会重新渲染整个组件,其行为与React的useState类似。 - 在早期版本中,
useReactive叫useState,其API设计尽可能保持与React的useState一致,减少用户的学习成本。但是很快发现useState与React内置名称冲突,导致需要进行重命名,所以后续版本中更名为useReactive。
getter & setter
useReactive还可以接受getter 和setter两个函数参数,用来获取和设置State中的组合属性。
getter:用来获取State中的组合属性。setter:用来设置State中的组合属性。
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]>可以指定getter和setter的泛型类型,这样在使用时会有更好的代码提示。 - useReactive(
<path string>)能自动类型推断,但是getter和setter需要手动指定泛型类型。
简单示例如下:
提示
useReactive还有一个别名useState,但是由于useState与React内置名称相同,使用时经常需要重命名,所以在AutoStore中使用useReactive来代替。
useAsyncReactive
如果状态是一个异步计算属性,也可以使用useAsyncReactive来处理。
useAsyncReactive返回是一个AsyncComputedValue对象,其包含了异步计算属性的状态信息。
更多异步计算的特性见异步计算属性。
直接读写
除了使用useReactive方法读写状态外,sotre.state返回的是一个响应式Proxy对象,可以直接读写也会触发内部的依赖收集和事件响应。
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类型的事件,如get或set等。
在某些场景下,我们可能不希望触发这些事件,可以使用silentUpdate方法。
store.silentUpdate(()=>{
store.state.age=100
})批量更新
一般情况下,更新多个状态时会触发多个更新事件。在React场景中,为了优化渲染,我们可能希望一次性更新多个状态,只触发一次渲染。此时就可以使用batchUpdate方法。
store.batchUpdate(()=>{
store.state.age=100
store.state.name="Fisher"
})关于更多的批量更新的技术细节见批量更新。
静默读取
正常访问状态时会触发get事件,如果不希望触发get事件,可以使用peep方法。
store.peep((state)=>{
return state.age
})
// 读取age不会触发get事件
store.peep("age") // 100以上方法不会触发get <age>事件。
update
update方法用来更新状态,其函数签名如下:
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机制,通过$方法创建一个信号组件,用来触发局部更新。