Skip to main content

Async state

Asynchronous updates are almost identical to synchronous updates, except that the payload is a function returning a promise.​


Let's begin with the following store:
import { createStore, enableAsyncActionPayloads } from 'olik'

type Todo = { id: number, title: string }
const store = createStore({
name: document.title,
state: { todos: new Array<Todo>() }
})

enableAsyncActionPayloads()

Reading async state​

store.todos
.$replace(() => fetch('http://api.dev/todos').then(res => res.json()))

Writing async state​

Let's start with the following function

const updateTodo = (todo: Todo) => () => fetch(`https://api.dev/todo/${todo.id}`, {
method: 'POST',
body: JSON.stringify(todo),
}).then(res => res.json())

Updating state (assuming that the API returnes the updated todo)

store.todos
.$find.id.$eq(3)
.$replace(updateTodo(todo));

Update state (assuming that the API does not return the updated todo)

updateTodo(todo)
.then(() => store.todos
.$find.id.$eq(3)
.$replace(todo));

Demo 🥚


Caching data​

The library uses your store as a cache and will not re-fetch for a specified number of milliseconds.

store.todos
.$replace(() => fetch('https://api/todos').then(res => res.json()),
{ cache: 1000 * 60 })

Demo 🥚


Invalidating caches​

The following ensures that any data cached on the selected node is re-fetched the next time a promise is used to populate this node.

store.todos
.$invalidateCache()

Demo 🥚

Eager updates​

You can make immediate updates, which will be rolled back if an error is thrown.

const updateUserStatus = (isAdmin: boolean) => {
store.user.isAdmin
.$replace(
() => fetch(`https://api/user/admin/${isAdmin}`).then(res => res.json()),
{ eager: isAdmin }
),
}

Co-locating endpoints using defineQuery()​

It may be more managable to define your endpoints, caching, and optimistic updates in one place.

endpoints.ts
import { defineQuery } from 'olik';

const fetchTodosQuery = defineQuery({
query: () => fetch(`https://api.dev/todos`).then(res => res.json()),
cache: 1000 * 60,
})

const updateTodoQuery = (arg: Todo) => defineQuery({
query: () => fetch(`https://api.dev/todos`).then(res => res.json()),
eager: arg,
});
component.ts
const fetchTodos = () => {
store.todos.$replace(...fetchTodosQuery);
}

const updateTodo = (todo: Todo) => {
store.todos.$find.id.$eq(3).$replace(...updateTodoQuery(todo));
}