Effect

The effect API complements the state API by providing a better way to react to multiple state changes based on what you need to be executed.

typescript
1type EffectSubscriber<T> = (value: T | undefined) => undefined | T2type EffectUnSubscriber = () => void3 4effect: <T>(sub: EffectSubscriber<T>) => EffectUnSubscriber

The state API allows you to subscribe to changes of its value.

javascript
1const [count, updateCount] = state(0, () => {2    // react to changes3})4 5updateCount(10)

This is great if you want to perform side effects related to a single state. To perform side effects for multiple states you will need the effect API.

javascript
1const [count, updateCount] = state(0)2 3effect(() => {4    // react to changes5})6 7updateCount(10)

EffectUnSubscriber

Some side effects can stay there continuosly as a global effect to something specific. Others need to be cleaned up whenever the scope where they are defined at goes away.

javascript
1const cleanEffect = effect(() => {2    // react to changes3})4 5cleanEffect()

How it works?

When you call a StateGetter inside the effect callback function, the effect becomes aware of the state and this detection is done synchronously.

The effect calls the provided callback as soon as its declared so it becomes aware of the states called inside.

javascript
1effect(() => {2    console.log(count()) // will log right away3})

The effect also batches updates which allows you to update multiple state at once and only have a single reaction.

javascript
1effect(() => {2    console.log(count(), total())3    // prints: 0 0 on initiation4    // prints: 1 1 on update of both values5})6 7setCount((prev) => prev + 1)8setTotal((prev) => prev + 1)

The batch update is useful but the effect also understand initialization which allows you to make a bunch calculation on load and only have it run once after you are done.

javascript
1effect(() => {2    console.log(count())3    // prints: 0 on initiation4    // print: 100 after the while loop completes5})6 7while (count() < 100) {8    setCount((prev) => prev + 1)9}

Caching

The effect allows you to return values that are cached and provided in the callback for the next time. This is great for tracking changes accross updates.

For example, perform a debounce effect on search value changes.

javascript
1const [search, setSearch] = state('')2const [searchResults, updateSearchResults] = state([])3 4effect((timer) => {5    clearTimeout(timer)6 7    // so the effect becomes aware of "search" state8    const searchValue = search()9 10    return setTimeout(async () => {11        const response = api.search({ searchValue })12 13        updateSearchResults(response.results)14    }, 300)15})

Async effect

The effect works synchronously. That's how it detects the states inside and caches data.

However, callback you provide to the effect can be asynchronous if you really want to.

javascript
1effect(async () => {2    try {3        const res = await fetch(4            `https://randomuser.me/api/?page=${count()}&results=10&seed=markup`5        )6 7        console.log(await res.json())8    } catch (e) {9        console.error(e)10    }11})

If you do so, the cached data will be the promise returned by the function and not the data you return from the async callback. This means you need to resolve the cached data to get the value.

javascript
1effect(async (res = Promise.resolve(0)) => {2    const result = count() + (await res)3 4    console.log(result)5 6    return result7})

Nested effect

You can nest effect to track different values. This allows for the body of your effect to react independently while different effects track different states.

javascript
1const unsub = effect(() => {2    console.log('outer', count())3    effect(() => {4        console.log('inner', count())5    })6})7 8unsub() // clears all effects

If you want to clear all effects, you can unsubscribe from the outer most effect and which will take care of all child effect for you. However, inner effects must be tracked by you as whenever the outer effect is called, a new inner effect will be created.

edit this doc