README
OLIK
Axiomatic, self-describing, in-line state-management
Olik allows you to comprehensively grok your state updates without ever leaving your component code.
- Its fluent, typesafe API dramatically improves the consistency of your state operations, eliminating ambiguity
- Debuggability is improved through auto-generated action types
- Nested stores allow you to manage and debug your component state with or without your application state.
- Transactions help you to group your state updates, avoiding overly abstract action types.
- Async updates, request de-duplication, optimistic updates, and caching are all built-in.
⚠️ NOTE: The below code demonstrates Olik without a framework.
There are, however, bindings for React, and
Angular
🌈 SET UP
Initializing your store couldn't be simpler while integration with the Redux Devtools extension is automatic.
export const { select, read } = createAppStore({
username: '',
favorite: {
foods: new Array<string>(),
movies: new Array<{ id: number, name: string, rating: number }>(),
},
});
✍️ WRITE STATE
Writes consist of a selection followed by an action allowing state-updates to be described for you.
select(s => s.username) // type: 'username.replace()'
.replace('Terence'); // replacement: 'Terence'
select(s => s.favorite.foods) // type: 'favorite.foods.insert()'
.insert(['Indian', 'Sushi']); // insertion: ['Indian', 'Sushi']
select(s => s.favorite.movies) // type: 'favorite.movies.filter().remove()'
.filterWhere(s => s.rating).isLessThan(2) // where: 'rating <= 2'
.remove(); // toRemove: [{ id: 2, name: 'Click', rating: 1 }, ...]
👓 READ STATE
State can be read from, listened to, and expensive derivations can be memoized.
const hobbies = read().favorite.hobbies;
const subscription = select(s => s.favorite.hobbies)
.onChange(e => console.log(e));
const derivation = derive(
select(s => s.foods),
select(s => s.hobbies),
).usingExpensiveCalc(
(foods, hobbies) => /* ...some expensive calculation that shouldn't repeat unnecessarily... */
)
↪️ TRANSACT
Perform multiple updates in one go to prevent unnecessary re-renders
transact( // type: 'username.replace(), favorite.foods.removeAll()'
() => select(s => s.username) // actions: [
.replace('James'), // { type: 'username.replace()', replacement: 'James' },
() => select(s => s.favorite.foods) // { type: 'favorite.foods.removeAll()' },
.removeAll(), // ]
);
⏲️ FETCH STATE
Pass in promises as payloads, bypass promise invocations temporarily, and perform optimistic updates
select(s => s.favorite.hobbies)
.replaceAll(() => fetchHobbiesFromApi(), { bypassPromiseFor: 1000 * 60 })
.catch(e => notifyUserOfError(e));
const newUserName = 'Jeff';
select(s => s.username)
.replace(() => updateUsernameOnApi(newUserName), { optimisticallyUpdateWith: newUserName })
.catch(e => notifyUserOfError(e));
🥚 NEST STORES
Each component's state can be managed and debugged with or without your application state.
select = createNestedStore({ // applicationStoreState = {
title: '', // /* ... */
description: '', // nested {
done: false, // TodoComponent: {
}, { // 1: { title: '', description: '', done: false }
storeName: 'TodoComponent', // }
instanceName: todoId // }
}); // }
select(s => s.done) // type: 'nested.TodoComponent.1.done.replace()'
.replace(true); // replacement: true