Store
Updating State

Updating State

Davstack Store provides two main methods for updating state: set and assign. Both methods use Immer under the hood, allowing you to update state immutably.

Example

import { store } from '@davstack/store';
 
const userStore = store()
	.state({
		name: 'John',
		age: 25,
		address: {
			street: '123 Main St',
			city: 'Anytown',
		},
	})
	.actions((store) => ({
		happyBirthday() {
			store.age.set(store.age.get() + 1);
		},
	}));
 
function AddressForm() {
	const userAddress = userStore.address.use();
 
	return (
		<form>
			<input
				value={userAddress.street}
				onChange={(e) => userStore.address.street.set(e.target.value)}
			/>
			<input
				value={userAddress.city}
				onChange={(e) => userStore.address.city.set(e.target.value)}
			/>
		</form>
	);
}

Best Practices

  • It's generally recommended to enapsulate state updates in actions, and avoid directly mutating state properties outside of actions. This helps to keep your codebase clean and maintainable.
  • Use the set method to update a single state property, and assign to update multiple properties at once.

The set Method

Use set to update the value of a state property.

import { store } from '@davstack/store';
 
const countStore = store(0);
 
countStore.set(5);

The set method can also be used to update nested state properties:

const userStore = store({
	name: 'John',
	age: 25,
	address: {
		street: '123 Main St',
		city: 'Anytown',
	},
});
 
userStore.address.city.set('Newtown');

The assign Method

The assign method is similar to set but is specifically designed for updating multiple properties of an object at once. It uses Object.assign under the hood.

const userStore = store({
	name: 'John',
	age: 25,
	address: {
		street: '123 Main St',
		city: 'Anytown',
	},
});
 
userStore.assign({
	name: 'Jane',
	age: 30,
});
ℹ️

When using assign, only the specified properties will be updated. Other properties will remain unchanged.

Updating Nested State with assign

You can also use assign to update multiple nested state properties:

userStore.address.assign({
	street: '456 Elm St',
	city: 'Newtown',
});

Note: the assign method only exists on object data type

Set with callback functions

The set function can accept a value or a callback function

Set value example: counterStore.set(5) Set callback function example: counterStore.set(()=>{...})

Datatype differences summary

The type of callback function changes depending on the state datatype.

Objects/Arrays: directly modify the draft, don't return anything Primitives: get previous value, return new value

(The types fully reflect which type of callback is required and therefore your IDE will guide you.)

Object / Array set callback details

Calling .set on an object/array uses immer under the hook, allowing you to mutate a draft copy of the state while maininaining immutability.

const userStore = store({ name: 'John', age: 20 });
 
// draft object uses immer
userStore.set((draft) => {
	draft.name = 'Jane';
	draft.age = 30;
});
 
const todosStore = store([
	{ id: 1, text: 'write docs' },
	{ id: 2, text: 'sleep' },
]);
 
todosStore.set((draft) => {
	draft.push('another task');
});
 
todosStore.set((draft) => {
	const firstTodo = draft[0];
	firstTodo.text = 'new text';
});

Primitives set callback details

Calling set on other data types behaves slightly differently.

You are given the previous value of the property and you must return the new value from the callback.

userStore.age.set((prevAge) => prevAge + 1);

Additional notes:

  • Non-draftable values (e.g., MediaRecorder or window) will be considered as primitive values

  • Since @davstack/store 1.3.0 root level array stores are fully supported