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()))
const Component = () => {
const future = store.todos
.$replace(() => fetch('http://api.dev/todos').then(res => res.json()))
.$useFuture()
return (
<>
{future.isLoading && <div>loading todos...</div>}
{future.wasRejected && <div>error loading todos: {future.error}</div>}
{future.wasResolved && future.storeValue.map(todo =>
<div key={todo.id}>{todo.title}</div>
)}
</>
)
}
@Component({...})
class MyComponent {
constructor(private httpClient: HttpClient) { }
todos$ = store.todos
.$replace(() => this.httpClient.get('http://api.dev/todos'))
.$asObservableFuture()
)
}
<ng-container *ngIf="todos$ | async; let todos;">
<div *ngIf="todos.isLoading">
Fetching todos...
</div>
<div *ngIf="todos.wasRejected">
Failed to fetch todos with error {{todos.error}}
</div>
<ul *ngIf="todos.wasResolved">
<li *ngFor="let todo of todos.storeValue">
{{todo.title}}
</li>
</ul>
</ng-container>
const future = store.todos
.$replace(() => fetch('https://api.dev/todos').then(res => res.json()))
.$observe({ fetchImmediately: true });
{#if $future.isLoading}
<div>loading...</div>
{/if}
{#if $future.wasResolved}
{#each $future.storeValue as todo}
<li>{todo.title}</li>
{/each}
{/if}
{#if $future.wasRejected}
<div>Sorry, the following error occurred: {$future.error}</div>
{/if}
<button on:click={future.get}>Re-fetch</button>
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));
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 })
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()
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));
}