Slice
Slices are central to managing your app's state. They bundle together actions, state fields, derived fields, and effects for cleaner and efficient state management.
To create a slice:
import { createKey } from '@nalanda/core';
const key = createKey('counter-slice', []);
// Initialize a Fields
const counterField = key.field(0);
// Define some actions
function incrementAction() {
return counterField.update((value) => value + 1);
}
// export the slice with all the fields and actions
// you want to expose to the rest of the app
export const counterSlice = key.slice({
counter: counterField,
incrementAction,
});
Always be selective when exporting fields and actions. It's a best practice to expose only what's necessary, keeping rest internal.
Registering a Slice
For a slice to be operational, it must be registered with the store:
import { createStore } from '@nalanda/core';
const store = createStore({
slices: [counterSlice],
});
Accessing the field data
You can access the exposed field's data by calling .get
on the slice:
export const counterSlice = key.slice({
// list all the fields you want to expose
counter: counterField,
doubleCounter: doubleCounterField,
incrementAction,
});
// Accessing the exposed data
const { counter } = counterSlice.get(store.state);
console.log(counter); // 0 (initial value)
Updating Slice data
You can call an action directly on the slice to update the state:
// Dispatching the action
store.dispatch(
counterSlice.incrementAction()
);
// state will now be updated
const { counter, doubleCounter } = counterSlice.get(store.state);
console.log(counter); // 1
console.log(doubleCounter); // 2
Usage
You can utilize slices in UI components, effects, or within other slices wherever there's access to the store or store state.
Within Another Slice
Below, the counterSlice
is utilized within another slice's action:
import { createKey } from '@nalanda/core';
import { counterSlice } from './counter-slice';
// Pass counterSlice as a dependency
const key = createKey('is-odd-even', [counterSlice]);
const isOdd = key.derive((state) => {
const { counter } = counterSlice.get(state);
return counterField % 2 === 0;
});
const isEven = key.derive((state) => {
const { counter } = counterSlice.get(state);
return counter % 2 === 1;
});
export const isOddEvenSlice = key.slice({
isOdd,
isEven,
});
Within an Effect
You can use a slice in another slice's Effect:
import { counterSlice } from './counter-slice';
// Pass counterSlice as a dependency
const key = createKey('is-odd-even', [counterSlice]);
key.effect((store) => {
const { counter } = counterSlice.get(store.state);
if (counter % 2 === 0) {
console.log('Even');
} else {
console.log('Odd');
}
});
In a React component
You can read data from Slice in a React component:
import { useStore } from '@nalanda/react';
import { counterSlice } from './counter-slice';
export function Counter() {
const store = useStore();
const { counter } = counterSlice.get(store.state)
return (
<div>
<p>Counter: {counter}</p>
</div>
);
}
For more information on using slices in React components, see usage with React.
Best practices
- Export slice only: Only export the slice and nothing else from the file:
// ❌ Don't do this - fields should never be exported
import { counterField } from './counter-slice';
// ✅ Do this - use slice to access data in fields
import { counterSlice } from './counter-slice';
// ✅ to access the counter field data
counterSlice.get(store.state).counter; // 0
- One slice per file: Keep a single slice per file to streamline locating slices and their dependencies.
// ❌ Don't do this - multiple slices in a single file
const oddSlice = key.slice({
isOdd,
});
const evenSlice = key.slice({
isEven,
});
// ✅ Do this - one slice per file, per key
const oddEvenSlice = key.slice({
isOdd,
isEvent
});