快速入门
AutoStore
功能强大,易于使用。以下通过构建一个简单书店商城的例子来展示如何使用AutoStore
,体验AutoStore
极致优雅和强大的功能。
第1步: 安装
npm install @autostorejs/react
npm install @autostorejs/devtools
yarn add @autostorejs/react
yarn add @autostorejs/devtools
pnpm add @autostorejs/react
pnpm add @autostorejs/devtools
安装@autostorejs/devtools
可以让开发者使用chrome
的Redux DevTools Extension来调试AutoStore
的状态。
第2步: 创建Store
使用createStore
来创建一个Store
。
import { createStore } from '@autostorejs/react';
const store = createStore({
orders: [
{
book: 'AutoStore最佳实践',
price: 39.9,
count: 1
}
]
});
- 通过
store.state
可以访问状态树,自动进行类型推断。
第3步: 创建计算属性
接下为,我们需要计算订单的小结
和总计
,只需要使用同步计算即可。
import { createStore } from '@autostorejs/react';
const store = createStore({
orders: [
{
book: 'AutoStore最佳实践',
price: 39.9,
count: 1,
// 小计
total: (scope)=>scope.price*scope.count
}
],
// 总计
total: (scope)=>scope.orders.reduce((acc,cur)=>acc+cur.total,0)
});
total
是一个计算属性,其值是orders
的total
的和,在创建时会自动收集依赖,当price
或count
变化时会自动重新计算。- 使用
$('状态路径')
来创建一个信号组件,当状态变化时,会自动更新。详见信号组件。
运行效果如下:
第4步: 创建异步计算属性
接下我们为订单增加一个折扣(discount)
字段。
订单的折扣是动态的,其计算很复杂,由后台的业务逻辑决定,可能会根据订单的数量、种类、时间、用户是否是VIP等来决定,因此我们设计折扣(discount)
字段为一个异步计算属性。
import { createStore } from '@autostorejs/react';
const store = createStore({
orders: [
{
book: 'AutoStore最佳实践',
price: 39.9,
count: 1,
// 小计
total: (scope)=>scope.price*scope.count
}
],
// 折扣: 向后台请求折扣
discount: computed(async (scope)=>{
// 如await fetch(`/api/discount?userId=1&total=${scope.total}....`)
await delay(2000)
return (0.5 + Math.random()).toFixed(2)
},['orders.*.total'],{async:true,initial:0.9}),
// 总计
total: computed(async (scope)=>{
return scope.orders.reduce((acc,cur)=>acc+cur.total,0)*scope.discount.value
},['discount'],{async:true})
});
注意:
discount
是一个异步计算属性,其计算函数是一个异步函数,其返回值会写入store.state.discount
,这就是AutoStore
与其他状态库最大不同之处,即原地计算。
运行效果如下:
在以上示例中,我们声明了两个异步计算属性discount
和total
discount
依赖orders.*.total
,即依赖了orders
数组中的total
属性。当订单的数量变化时,触发discount
的计算函数,计算函数会向后台请求折扣,然后返回新的折扣值。(在上例中,我们模拟请求折扣的过程,使用了delay
函数,实际项目中,我们可能会使用fetch
或axios
等来请求折扣)。total
则依赖discount
,即依赖了discount
的值。当discount
变化时,触发total
的计算函数,重新计算总计。
WARNING
上例中异步计算discount
需要2000ms
才可以看到discount
和total
的变化。期间界面没有任何变化,显然这是不友好的。一般最好是在计算折扣过程中时,显示一个loading
状态,告诉用户正在请求折扣。当折扣请求完成时,再显示折扣值。
因此, AutoStore
提供了loading
、error
、timeout
、retry
、cancel
、progress
等高级功能。
第5步: 控制异步计算
在上一步中,当我们调节订单数量count
时,需要2000ms
才可以看到discount
和total
的变化。期间界面没有任何变化,显然这是不友好的,我们需要在请求折扣时,显示一个loading
状态,告诉用户正在请求折扣。当折扣请求完成时,再显示折扣值。
这时,我们就可以开始引入异步计算
的各种高级功能。让我们先在计算时显示loading
状态。
与同步计算属性相比,创建异步计算属性discount
时,会转换为一个AsyncComputedValue
对象回写到state.state.discount
,此时访问state.state.discount
,其值如下:
{
loading : false,
progress: 0,
timeout : 0,
error : null,
retry : 0,
value: 0.9,
run:()=>{...}
}
也就是说,任何异步计算属性,均会有一个loading
字段,当计算函数运行时,loading
为true
,当计算函数运行完成时,loading
为false
。
因此,在本例中,我们可以通过discount.loading
来判断是否正在请求折扣,如果是,则显示loading
状态。
运行效果如下:
调节订单数量时,可以看到discount
和total
的变化,同时显示了loading
状态。当折扣请求完成时,loading
状态消失,显示折扣值。
一切都如预期工作。当然,实际项目只显示loading
状态是不够的,一般我们还需要增加以下功能:
- 超时处理:向服务器请求折扣可能会出错。
- 倒计时: 可能我们想显示一个倒计时,告诉用户还有多久请求完成。
- 重试机制:如果请求折扣失败,我们可能会重试请求。
- 错误处理:如果请求折扣失败,我们需要显示错误信息。
- 可取消:如果用户取消了订单,我们需要取消请求。
- 处理进度:可能我们想提供一个请求进度
0-100%
,告诉用户请求进度。
所有这些功能AutoStore
均为你准备好了,开箱即用,简单快捷,详见异步计算
第6步: 增加订单
以上我们只有一条订单,接下来我们增加多条订单,看看AutoStore
是如何处理多条订单的。
- 首先使用
useForm
创建一个表单用来收集用户输入。
const { state:newOrder, Form:NewOrderForm } = useForm({
book:'精通AutoStore',
price:10,
count:1
})
useForm
内部也创建了一个AutoStore
用来保存新订单的数据。
- 使用
store.state.orders.push
将新订单添加到orders
数组中。
<Button onClick={()=>{
store.state.orders.push({
...newOrder,
total:calcOrderTotal
})
}}>Add</Button>
运行效果如下:
WARNING
AutoStore
提供了非常强大的表单双向绑定能力,详见表单绑定查看更多内容.
小结
- 使用
createStore
或useStore
可以创建任意嵌套的响应式状态对象。 - 在组件中使用
useReactive
可以访问状态数据,并在状态变化时自动重新渲染。 - 使用
$('状态路径')
可以创建一个信号组件,将渲染更新限制在较细的范围内。 - 使用
computed
可以创建一个计算属性,其值是根据其他状态计算而来,当依赖状态变化时,会自动重新计算。 - 异步计算支持
loading
、error
、timeout
、retry
、cancel
、progress
等高级功能。 useForm
可以将表单元素与store
双向绑定,非常方便。