State management is the process of controlling how the state is updated and passed from one component to another. Regardless of the library or framework we use, state management is an essential part of developing JavaScript applications.
When working with a library like Redux, we have to set up actions, reducers, and <Provider store={store}>
to get our application state working. Elf simplifies state management for us by reducing the hassle of creating repetitive, boilerplate code in our applications.
In this article, we will learn about Elf, a new state management library. We’ll cover its features and how we can use it to manage the state in our applications.
State management is one of the challenging aspects of developing applications. When building web applications, we can store the most primitive and basic states locally in the components that need them. However, as our applications grow bigger, managing shared states across multiple components becomes difficult.
When this happens, state management becomes increasingly complex, and we begin to have issues such as prop drilling and state falling out of sync among different UI components.
These, and more, are the problems developers solve by building state management libraries such as Elf.
Elf is a reactive and immutable state management library built on top of RxJS. Elf provides us with a wide arrange of tools to manage our state. Because of this, there is some terminology we should know, like observables, observers, and subscriptions.
Observables are objects that can emit data over a period of time. They function as wrappers around data sources or stream of values
Observers are consumers of the data observables store. They execute a piece of code if the data being observed is mutated or if an error occurs, and react to state changes. They also implement up to three methods: next
, error
, and complete
. We will not look at these in detail because they are specific to RxJS and therefore beyond the scope of this article.
Subscriptions are how we connect observers to observables. Observers subscribe to observables, watch for any changes in the data, and react to those changes.
Run the command below in your terminal to install Elf:
npm i @ngneat/elf
At the center of every Elf application is the “store”. A store is a container that holds our application’s state. We can think of a store like a database.
An Elf store takes an object that contains three properties — a state
, a name
, and config
.
Let’s see how the store works by creating a state with it:
import { Store, createState, withProps } from '@ngneat/elf'; const { state, config } = createState({ data: { name: "John", email: "[email protected]" }, }); const formStore = new Store({ state, name: 'userData', config });
The createState
method returns the state
and config
objects, which we use to create the store.
Elf stores are observables, so we can subscribe to them and get their values like so:
formStore.subscribe((state) => {console.log(state)});
select
operatorThe select
operator enables us to select a “slice” (or a specific part) of a store.
For example, say we had the store below:
const dummyStore = { todos: [ { text: "Learn Elf", completed: true }, { text: "Use Elf", completed: false }, ], users: [ { name: "Jack" }, { name: "Jill" } ], };
Each key/value pair in the dummyStore
object is a slice; dummyStore.todos
and dummyStore.users
are slices.
Now let’s see how the operator works:
import { select } from '@ngneat/elf'; const userEmail = formStore.pipe(select((state) => state.data.email));
The select
operator only runs when the state changes or when there is a new reference to the selected state.
We use the pipe operator to chain multiple functions together to keep our code readable.
Elf entities are unique types of Elf stores. Entities act in the same manner as tables in a database, and we can store large collections of similar data in entities.
For example, if we had a clothing ecommerce store with data on shoes, shirts, and sneakers, entities would be the right place to store that data.
We can set up entities in Elf using the ngneat/elf-entities
package. We can also run queries and mutations on these entities using the different query and mutation methods Elf provides, which I will explain below.
Elf provides several queries that we can use to run query operations on entities. Let’s look at some of these query operations.
The selectFirst
query returns the first entity in a store. You can use it like so:
import { selectFirst } from '@ngneat/elf-entities'; const users = usersStore.pipe(selectFirst());
The selectLast
query returns the last entity in a store:
import { selectLast } from '@ngneat/elf-entities'; const users = usersStore.pipe(selectLast());
The selectAll
query returns all the entities in a store’s collection:
import { selectAll } from "@ngneat/elf-entities"; const users = usersStore.pipe(selectAll());
There are several methods we can use to mutate Elf entities.
We can use the addEntities
mutation to add entities to a store. We can add multiple entities to a store by passing an array of data to addEntities
like so:
import { addEntities } from '@ngneat/elf-entities'; //add a single entity usersStore.update(addEntities(user)); //add multiple entities usersStore.update(addEntities([user1, user2]));
We can add single or multiple entities from a store using the deleteEntities
mutation. To delete multiple entities, we pass in an array containing the IDs of the entities we want to delete:
import { deleteEntities } from '@ngneat/elf-entities'; //delete a single entity usersStore.update(deleteEntities(id)); //delete multiple entities usersStore.update(deleteEntities([id, id]));
We can update the entities in a store using the updateEntities
mutation:
import { updateEntities } from '@ngneat/elf-entities'; usersStore.update(updateEntities(id, { name }));
persistState
functionThe persistState
function enables us to persist some of the app’s state by saving it to a browser’s local or session storage.
We need to install the elf-persist-state
package before we can use persistState
function. Run the command below in your terminal to install the package:
npm i @ngneat/elf-persist-state
Now, let’s see how persistState
works:
import { Store, createState, select } from "@ngneat/elf"; import { persistState, localStorageStrategy, sessionStorageStrategy, } from "@ngneat/elf-persist-state"; const { state, config } = createState({ data: { name: "John", email: "[email protected]" }, }); const formStore = new Store({ state, name: "userData", config }); export const persist = persistState(formStore, { key: "auth", storage: localStorageStrategy, });
The persistState
function takes two parameters. The first is the store we want to persist, and the second is an options
object. We define the key
and storage
in the object.
The key
is the name under which we save the persisted state. The key
acts as the state’s label or identifier. The storage
is where we define the strategy we want to use to persist the state.
The elf-persist-state
package provides two storage strategies, localStorageStrategy
and
sessionStorageStrategy
. We can choose the strategy that best fits our application’s needs. Here, we used the localStorageStrategy
.
We have gotten a brief intro to Elf and some of its functionality. However, Elf has a lot more to offer. A big selling point of Elf is its low learning curve. Compared to alternative state management solutions such as Redux, Elf is a beginner-friendly library, and easy to work with.
An issue I have with Elf is its documentation. The documentation is lacking in a lot of useful context that will make the library even easier to learn. There is also a scarcity of examples that would help show Elf’s full capabilities.
I would use Elf because of the low learning curve, but only for personal or pet projects. Take note that it is still a relatively new library, so check it out in the docs and familiarize yourself with this awesome state management framework.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowReact Native’s New Architecture offers significant performance advantages. In this article, you’ll explore synchronous and asynchronous rendering in React Native through practical use cases.
Build scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
3 Replies to "Managing state with Elf, a new reactive framework"
No matter how many state libraries I read about or test myself, nothing is as simple and usable as Zustand
Why not use zustand? Same features with smaller size
can you even use zustand on angular though?
I think for React Zustand is a no brainer but I have found Elf to work super well with angular.