A really small (~3kb/~1kb gziped) library to create an Entity Adapter¹² for Zustand.
It also allows you to create a convenient store that includes any additional structure and the actions and selectors to be used to manage the entities.
To install this library in your project, use npm :
npm install zustand-entity-adapter
or yarn:
yarn add zustand-entity-adapter
Here is a basic example of how to use the library.
Your adapter provides an API for manipulating and querying a normalized collection of state instances of the same type. It will allow you to create a normal Zustand store, and additionally to configure the actions and selectors for it.
import { create } from "zustand";
import { createEntityAdapter } from "zustand-entity-adapter";
// Define the entity
interface User {
id: number;
name: string;
}
// Create the entity adapter
const entityAdapter = createEntityAdapter<User, number>();
// Create the entity store
const useUserStore = create(entityAdapter.getState);
// Configure the entity actions
const userActions = entityAdapter.getActions(useUserStore.setState);
// Configure the entity selectors
const userSelectors = entityAdapter.getSelectors();
You will be able to use the store as a regular store, and the selectors and actions without any additional binding.
const UserList: FC = () => {
const users = useUserStore(userSelectors.selectAll);
return (
<ul>
{users.map((user) => (
<li>{user.name}</li>
))}
</ul>
);
};
const AddUserButton: FC<{ user: User }> = ({ user }) => {
return <button onClick={() => userActions.addOne(user)}>Add user</button>;
};
Alternatively, you can create an entity store and enjoy most of this configuration out the box.
// Create the entity store - One less type argument
const useUserStore = createEntityStore<User>();
// Configure the entity actions - No function calls, only properties
const userActions = useUserStore.actions;
// Configure the entity selectors
const userSelectors = useUserStore.selectors;
The store and helpers can be used as described previously.
Creates an entity adapter for managing a normalized collection of entities of type Entity
. This adapter provides methods for querying and updating the state, as well as actions for manipulating the entities.
-
Parameters:
options.idSelector
(optional): A function that returns the entity ID.options.sort
(optional): A function to sort the entities.
-
Methods:
getState()
: Creates and returns the entity state.getActions(setState: SetState)
: Returns a set of actions to manipulate the entity state.getSelectors()
: Provides selectors to query the state.
The selectors provided by getSelectors()
are useful for querying the state.
selectIds(state: State)
: Returns an array of entity IDs.selectEntities(state: State)
: Returns a dictionary of entities by ID.selectAll(state: State)
: Returns an array of all entities.selectTotal(state: State)
: Returns the total count of entities.selectById(id: Id)
: Returns a specific entity by its ID.
The actions provided by getActions()
allow manipulation of entities within the store.
addOne(entity: Entity)
: Adds a new entity to the collection.addMany(entities: Entity[])
: Adds multiple entities to the collection.setOne(entity: Entity)
: Replaces an entity in the collection with the provided one.setMany(entities: Entity[])
: Replaces multiple entities in the collection.setAll(entities: Entity[])
: Replaces all entities with the provided set.updateOne(update: Update<Entity, Id>)
: Partially updates a single entity.updateMany(updates: Update<Entity, Id>[])
: Partially updates multiple entities.upsertOne(entity: Entity)
: Inserts or updates a single entity.upsertMany(entities: Entity[])
: Inserts or updates multiple entities.removeOne(id: Id)
: Removes an entity by its ID.removeMany(ids: Id[])
: Removes multiple entities by their IDs.removeAll()
: Removes allentities.
Creates an entity store using zustand
, providing a full set of selectors and actions for managing entities.
-
Parameters:
options?
: (optional) Configuration for sorting or ID selection. The same options thecreateEntityAdapter
has.stateCreator?
: (optional) Function to create additional state in the store.actionsCreator?
: (optional) Function to create custom actions.
-
Properties: The
createEntityStore
returns a regular Zustand store, with a state to manage the entities, and this properties:actions
: The actions to manage the entities collection. The result of calling theEntityAdapter#getActions
method.selectors
: The selector to query the entities collection. The result of calling theEntityAdapter#getSelectors
method.
-
Examples:
-
Basic store creation:
const useStore = createEntityStore<Entity>();
-
Store with additional state:
const useStore = createEntityStore( (): EntityState => ({ selectedUser: undefined }), );
-
Store with additional state and custom actions:
const useStore = createEntityStore( (): EntityState => ({ selectedUser: undefined, }), (set): EntityActions => ({ selectUser: (user) => set({ selectedUser: user }), }), );
-
Store with custom sorter:
const useStore = createEntityStore<Entity>({ sort: sorter });
-
Store with custom id selector (required if the model doesn't have an id property):
interface Product { sku: string; } const useStore = createEntityStore<Product, string>({ idSelector: (product) => product.sku, });
-
If you want to contribute to this project:
- Fork the repository.
- Create a new branch (
git checkout -b feature/new-feature
). - Make the changes and commit them (
git commit -m 'Add new feature'
). - Push the changes to the branch (
git push origin feature/new-feature
). - Open a Pull Request.
This project is licensed under the MIT License. See the LICENSE
file for more details.
This project could not exist without the amazing work from the @ngrx
and the RTK
teams.