Simple universal composable asynchronous state management utilities.
- CJS + ESM ✅
- Lightweight ✅
- Simple and easy to use ✅
- Retry mechanism ✅
- Caching ✅
- Structural sharing ✅
- Framework agnostic (note: BYO framework wrappers) ✅
Managing asynchronous state is hard, on the backend the data access patterns we utilize varies wildly depending on the type of database we use and on the frontend we need to worry about keeping our data up to date, caching, network failures, and structural sharing to prevent rerenders.
incremental
standardizes and simplifies the data access patterns we utilize on the backend and frontend by providing simple composable utilities allowing us to opt-in to the functionality we need.
The central idea is to utilize state-based CRDT's - a type of data structure which provides strong eventual consistency - to manage all data access, any changes to data are dispatch
ed and applied by dispatch
using onChange
.
On the frontend, we often have to fetch data from the backend and then apply updates on a button click:
const user = createCRDT({
initialValue: await service.getUserById,
onChange: makeMonitor({
fetchFn: service.putUser,
onFetching: toggleLoadingIndicator,
// For `fetchFn` to throw `onError`
// `onError` has to throw
onError: displayError,
}),
});
const onSubmit = updates => user.dispatch(updates, { onChange: setUser });
or, sometimes we have incremental updates from multiple sources which need to be applied:
const chatMessages = createCRDT({
initialValue: await service.getChatMessages(currentUser, recipient),
onChange: makeMonitor({
// makeRetry exponentially backs off
fetchFn: makeRetry({
retryFn: service.putChatMessage,
}),
onFetching: toggleLoadingIndicator,
}),
});
const newMessagesSocket = new WebSocket(wsUri);
newMessagesSocket.onmessage = event =>
dispatch(
previousChat => {
const message = JSON.parse(event.data);
previousChat.messages.push(message.data);
return previousChat;
},
// If `isPersisted`, then `chatMessages.onChange`
// is not invoked
{ isPersisted: true },
);
const onSubmitChatMessage = newMessage =>
chatMessages.dispatch(
previousChat => void previousChat.messages.push(newMessage),
);
More information in:
src/crdt/crdt.spec.ts
src/monitor/monitor.spec.ts
src/retry/retry.spec.ts
- Contributions are welcome, just make a pull request
"And I don't have a name, I don't have a name, no"