Svelte store 模拟useState

我们先来看个例子:

Svelte

1
2
3
4
5
<script>
let name = 'John';
</script>

<h1>Hello {name}</h1>

React

1
2
3
4
5
6
7
import { useState } from 'react';

export default function Name() {
const [name] = useState('John');

return <h1>Hello {name}</h1>;
}

从上面的例子可以看出,Svelte的反应性是非常的简洁的,只需要使用let进行声明即可。再看下React是我们熟悉的hooks的方式。

今天我们要谈的看标题名称就明白啥意思了,如果你觉得React的Hooks很好用,比如useState,也想在Svelte中这么使用,当然了只是想这样咱们不弹对错。我们应该怎么做呢?

咱们前面学习了Sveltestores大家还记得吧,最常使用的是writable,今天咱们先使用不常用的readable 来模拟。

还记得官方的例子吧,咱们再来看看:

1
2
3
4
5
6
7
8
9
10
11
import { readable } from 'svelte/store';

const time = readable(null, set => {
set(new Date());

const interval = setInterval(() => {
set(new Date());
}, 1000);

return () => clearInterval(interval);
});

这个例子是定时返回事件对象,从这个例子我们可以了解readable的使用规则

store = readable(value?: any, start?: (set: (value: any) => void) => () => void)

我们要是想使用它模拟useState,该怎么处理呢?

我们先来看看useState的语法

const [name,setName] = useState('John')

useState方法对外暴露两个参数,第一个参数是初始值名称,第二个是设置方法,下面开始模拟

1
2
3
4
5
import { readable } from 'svelte/store';

function useState(initValue){

}

首先引入readable,然后生命useState函数,传入初始值参数。然后把readable放到useState中

1
2
3
4
5
6
7
8
9
10
11
12
13
import { readable } from 'svelte/store';

function useState(initValue){
const nameStore = readable(initValue, set => {

});

return [nameStore,setValue]
}

export {
useState
}

上面就是基本骨架了,下面继续完善。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { readable } from 'svelte/store';

function useState(initValue){
let setValue
const nameStore = readable(initValue, set => {
setValue = set
})


return [nameStore,setValue]
}
export {
useState
}

setValue是设置内容的,我们看看readable的官方例子就知道,我们把set方法赋给setValue即可。

好了我们试试效果吧:

1
2
3
4
5
6
7
8
<script>
import { useState } from './hooks.js'
let nameVal = 'world';
const [name,setName] = useState(nameVal)
setName("useState")
</script>

<h1>Hello {name}</h1>

结果是报错:setName is not a function

我们需要让nameStore订阅下即可

1
2
3
4
5
6
7
8
9
10
11
12
13
import { readable } from 'svelte/store';

function useState(initValue){
let setValue
const nameStore = readable(initValue, set => {
setValue = set
})
nameStore.subscribe(value => {})
return [nameStore,setValue]
}
export {
useState
}

同时我们还需要修改下,还记得之前讲的自动订阅吧,就是加$

1
<h1>Hello {$name}</h1>

REPL

通过这个例子我们会发现使用writable 也是可以来模拟的,我们来看看:

1
2
3
4
5
6
7
8
9
10
11
import { writable } from 'svelte/store';

function useState(initValue){
const nameStore = writable(initValue)
let setValue = ((val) => {
nameStore.update((currentVal) => currentVal = val)
})
return [nameStore, setValue]
export {
useState
}

架子基本上差不多的,区别就是把readable换成了writable,这样有没有问题呢?大家知道writable 声明后的nameStore是可以使用$直接改的

1
2
3
4
const [names,setNames] = useState2("world")
setTimeout(()=>{
$names = "hooks"
},1000 * 3)

所以上面的模拟还是有点问题,还得我们前面有篇文章又讲到一个方式的,我们使用派生(derived)来解决。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { writable,derived } from 'svelte/store';

function useState(initValue){
const nameStore = writable(initValue)
let setValue = ((val) => {
nameStore.update((currentVal) => currentVal = val)
})
const readableStore = derived(nameStore, $nameStore => $nameStore)
return [readableStore, setValue]
}
export {
useState
}

这样调整之后再执行下面代码

1
2
3
4
const [names,setNames] = useState2("world")
setTimeout(()=>{
$names = "hooks"
},1000 * 3)

就会报错了,需要改成

1
2
3
4
const [names,setNames] = useState2("world")
setTimeout(()=>{
setNames("hooks")
},1000 * 3)

ok,你对实现一个useReducer感兴趣你也可以自己动手实现下。

当然这里就是为了说明Svelte的store的灵活,同时也是为了好玩,真正使用的话,如果这些写是画蛇添足了~


Svelte store 模拟useState
http://yoursite.com/2022/08/01/svelte-2/
作者
昂藏君子
发布于
2022年8月1日
许可协议