Recoil

Language: JavaScript

State Management

Recoil was developed by Facebook to address complex state management needs in React applications. It introduces atoms (pieces of state) and selectors (derived or computed state), providing a more declarative and scalable approach compared to prop drilling or using large global stores.

Recoil is a state management library for React that provides a simple and flexible way to share state across components. It allows for fine-grained state updates, derived state, and asynchronous selectors while integrating seamlessly with React's concurrent features.

Installation

npm: npm install recoil
yarn: yarn add recoil

Usage

Recoil uses atoms to represent shared state and selectors to derive or transform state. Components subscribe to atoms or selectors via hooks, automatically re-rendering when state changes. It supports both synchronous and asynchronous derived state.

Creating an atom

import { atom } from 'recoil';

export const countState = atom({
  key: 'countState', // unique ID
  default: 0,
});

Defines a Recoil atom representing a piece of state called `countState` with a default value of 0.

Using an atom in a component

import { useRecoilState } from 'recoil';
import { countState } from './store';

function Counter() {
  const [count, setCount] = useRecoilState(countState);
  return (
    <div>
      <button onClick={() => setCount(count - 1)}>-</button>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

Uses the `useRecoilState` hook to read and update the `countState` atom in a React component.

Derived state with selectors

import { selector } from 'recoil';
import { countState } from './store';

export const doubleCountState = selector({
  key: 'doubleCountState',
  get: ({ get }) => {
    const count = get(countState);
    return count * 2;
  },
});

Defines a selector that derives a new piece of state (`doubleCountState`) based on the value of `countState`.

Using an async selector

import { selector } from 'recoil';

export const userDataState = selector({
  key: 'userDataState',
  get: async () => {
    const response = await fetch('https://api.example.com/user');
    return response.json();
  },
});

Shows how to define a selector that fetches data asynchronously.

Multiple atoms and selectors

import { atom, selector, useRecoilValue } from 'recoil';
const firstNameState = atom({ key: 'firstName', default: '' });
const lastNameState = atom({ key: 'lastName', default: '' });
const fullNameState = selector({ key: 'fullName', get: ({ get }) => {
  return `${get(firstNameState)} ${get(lastNameState)}`;
}});

Combines multiple atoms into a derived state (full name) using a selector.

Error Handling

Duplicate key error: Ensure that every atom or selector has a unique `key` string.
Components not updating: Check that you are using Recoil hooks (`useRecoilState`, `useRecoilValue`) and not React state for the same value.
Selector fetch fails: Handle errors in async selectors using try/catch and consider fallback states.

Best Practices

Use atoms for shared state and selectors for derived or computed state.

Keep atoms small and focused to avoid unnecessary re-renders.

Use unique `key` values for all atoms and selectors.

Leverage asynchronous selectors for data fetching and caching.

Avoid excessive use of global atoms; local component state is sometimes simpler.