Core Concepts
Stores
Section titled “Stores”Stores model and control the state of your application. Listeners can subscribe to the store and react to changes, e.g. by updating the UI. When you create a store you define the initial state and a set of actions. Actions are functions that update the state in a predictable way.
Example
Section titled “Example”Here’s how you can create a simple counter store using createStore:
import { createStore } from "dharma-core";
const store = createStore({ initialState: { count: 0 }, actions: ({ set }) => ({ increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), }),});
// Actions can be destructured for easy useconst { increment, decrement } = store.actions;
// You can subscribe to state changesconst unsubscribe = store.subscribe((state) => console.log(state));
// And update the state by calling actionsincrement(); // { count: 1 }decrement(); // { count: 0 }
// When you no longer need to listen to state changes you can unsubscribeunsubscribe();Integration
Section titled “Integration”The core library is designed to work in any JavaScript environment and can be integrated with any UI framework.
For React applications, Dharma provides a dedicated package, dharma-react, with hooks to simplify integration.
React example
Section titled “React example”The useStore hook lets you subscribe to a store from within a React component.
The component will re-render automatically whenever the store’s state changes.
import { useStore } from "dharma-react";import { store, increment, decrement } from "./store";
function Counter() { const { count } = useStore(store);
return ( <div> <div>{count}</div> <button onClick={decrement}>-</button> <button onClick={increment}>+</button> </div> );}Persisting state
Section titled “Persisting state”Dharma stores can easily persist their state to a key-value storage, like localStorage in the browser or AsyncStorage in React Native.
To enable persistence, set persist: true and provide a unique key when creating the store.
This key is used to identify the store in the storage.
import { createStore } from "dharma-core";
const store = createStore({ persist: true, key: "counter", // Unique key for storage initialState: { count: 0 }, actions: ({ set }) => ({ increment: () => set((state) => ({ count: state.count + 1 })), }),});Derived state
Section titled “Derived state”Sometimes, you need to compute a value based on the state of a store. Dharma provides the derive function for this purpose.
Derived state is memoized, meaning it will only be recomputed when its dependencies change.
For granular control over dependencies you can provide a dependencyFn.
Example
Section titled “Example”import { derive } from "dharma-core";import { store, increment } from "./store";
// This derived store will contain the squared value of 'count'const squared = derive(store, (state) => state.count * state.count);
// You can subscribe to derived state just like a regular storesquared.subscribe((state) => console.log(state));
increment(); // 1increment(); // 4Effects
Section titled “Effects”Effects allow you to run side effects in response to state changes, such as logging, analytics,
or interacting with other parts of your application. The createEffect function creates an effect that listens to a store.
Example
Section titled “Example”import { createEffect } from "dharma-core";import { store, increment } from "./store";
// This effect will log the count whenever it changesconst logger = createEffect(store, (state) => { console.log("count =", state.count);});
// Mount the effect to start listeninglogger.mount();
increment(); // count = 1increment(); // count = 2
// Unmount the effect to stop listeninglogger.unmount();Best practices
Section titled “Best practices”Dharma is unopinionated, but here are some recommendations to keep your state management clean and maintainable:
- Decouple Stores from the UI: Keep your store logic separate from your UI components. This makes your stores easier to test and allows you to reuse them with different UI frameworks.
- Single-Purpose Actions: Each action should have a single, clear purpose. This makes your state changes predictable and easier to debug.
- Domain-Specific Stores: Avoid creating one giant store for your entire application. Instead, create smaller stores that manage specific domains of your application’s state.