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
机制,通过$
方法创建一个信号组件,用来触发局部更新。