Effortless State Management in React with SigniaAs React applications grow in complexity, state management can become a significant challenge. While native tools like `useState` and `useContext` provide some relief, they often fall short when implementing common design patterns, such as a central, shared state consumed by multiple components. This is where Signia, a state management library that uses signals to solve these problems, comes into play.What is Signia?Signia is an original library

Simplifying State Management in React with Signals

The Challenge of State Management

As React applications grow in complexity, state management can become a significant challenge. While native tools like useState and useContext provide some relief, they often fall short when implementing common design patterns, such as a central, shared state consumed by multiple components.

Introducing Signia

Signia is a state management library that uses signals to solve these problems. It provides a new lazy reactivity model based on logical clocks, allowing for efficient calculation of computed values through incremental calculations. Additionally, internal clocks provide support for transaction rollbacks if required.

Understanding Signals

A signal is a value that changes over time, and its change events can trigger side effects. In other words, a signal is a pure, reactive value that can be observed for changes. It’s the responsibility of the signal’s library to observe these changes, notify subscribers, and trigger the required side effects.

Signia Core Concepts

Atom

An Atom in Signia represents the signals that correspond to the root state, i.e., the source of truth for your app. Its value can be read and updated, as well as built upon to create computed values.

const fruit = atom('fruit', 'Apple');

Updating an Atom

To update an Atom, use the set function:

set(fruit, 'Banana');

Computed Signals

Computed signals are derived from Atoms and have a dependency on them; their values are recomputed whenever the Atoms they depend on change.

const fruitCount = computed(() => {
  return fruit.value.length;
});

Updating a computed signal is not possible directly. However, updating any of its root Atoms will automatically update the computed signal:

set(fruit, 'Orange');
console.log(fruitCount.value); // Output: 6

React Bindings for Signia

The official React bindings are shipped in two packages: signia-react and signia-react-jsx.

Getting Hands-on with Signia

Setting up Signia

Install the Signia-specific libraries:

npm install signia-react signia-react-jsx

Setting up Chakra UI

Install Chakra UI and its peer dependencies:

npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion

Testing Reactivity with Signia

Create a simple counter app that uses Signia for state management:

import { useAtom } from 'ignia-react';

function Counter() {
  const count = useAtom(0);

  return (
    <div>
      Count: {count}
      <br />
      <button onClick={() => set(count, count + 1)}>Increment</button>
    </div>
  );
}

Designing the State

Design the state for our to-do list app:

class Todo {
  items = {};
  title = '';

  addItem(id, text) {
    this.items[id] = { id, text, completed: false };
  }

  markItemAsDone(id) {
    this.items[id].completed = true;
  }

  setTitle(title) {
    this.title = title;
  }
}

Creating the UI

Create the UI for our to-do list app:

import { useTodoFromContext } from './TodoContext';

function TodoList() {
  const todo = useTodoFromContext();

  return (
    <div>
      {todo.title}
      <ul>
        {Object.values(todo.items).map((item) => (
          <li key={item.id}>
            <input type="checkbox" checked={item.completed} />
            {item.text}
          </li>
        ))}
      </ul>
    </div>
  );
}

Sharing State between React Components

Use React Context to share state between different components:

// TodoContext.js
import { createContext, useContext } from 'eact';
import { atom } from 'ignia-react';

const TodoContext = createContext();

export function useTodoFromContext() {
  return useContext(TodoContext);
}

// App.js
import { TodoContext } from './TodoContext';

function App() {
  const todo = atom(new Todo());

  return (
    <TodoContext.Provider value={todo}>
      <TodoList />
    </TodoContext.Provider>
  );
}

Leave a Reply