logo
eng-flag

Recoil Cheatsheet

Table of Contents

  1. Introduction
  2. Core Concepts
  3. Atoms
  4. Selectors
  5. Hooks
  6. Async Queries
  7. State Persistence
  8. Dev Tools
  9. Best Practices
  10. Advanced Patterns

Introduction

Recoil is a state management library for React applications, developed by Facebook. It provides a way to create a data-flow graph that efficiently updates React components.

Core Concepts

RecoilRoot

Wrap your React app with RecoilRoot to use Recoil:

import { RecoilRoot } from 'recoil';

function App() {
  return (
    <RecoilRoot>
      {/* Your app components */}
    </RecoilRoot>
  );
}

Atoms

Atoms are units of state in Recoil. They're updatable and subscribable.

Creating an Atom

import { atom } from 'recoil';

const counterState = atom({
  key: 'counterState',
  default: 0,
});

Using an Atom

import { useRecoilState } from 'recoil';

function Counter() {
  const [count, setCount] = useRecoilState(counterState);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

Selectors

Selectors are pure functions that derive state based on atoms or other selectors.

Read-only Selector

import { selector } from 'recoil';

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

Writable Selector

import { selector } from 'recoil';

const fahrenheitState = selector({
  key: 'fahrenheitState',
  get: ({get}) => {
    const celsius = get(celsiusState);
    return (celsius * 9) / 5 + 32;
  },
  set: ({set}, newValue) => {
    const celsius = ((newValue - 32) * 5) / 9;
    set(celsiusState, celsius);
  },
});

Hooks

Recoil provides several hooks for interacting with atoms and selectors.

useRecoilState

const [value, setValue] = useRecoilState(myAtom);

useRecoilValue

const value = useRecoilValue(myAtom);

useSetRecoilState

const setValue = useSetRecoilState(myAtom);

useResetRecoilState

const resetValue = useResetRecoilState(myAtom);

Async Queries

Recoil can handle asynchronous data fetching using selectors.

const userInfoQuery = selector({
  key: 'userInfoQuery',
  get: async ({get}) => {
    const userId = get(userIdState);
    const response = await fetch(`/api/user/${userId}`);
    return response.json();
  },
});

function UserInfo() {
  const userInfo = useRecoilValue(userInfoQuery);
  
  if (userInfo.loading) {
    return <div>Loading...</div>;
  }
  
  return <div>{userInfo.name}</div>;
}

State Persistence

Recoil allows for easy state persistence using atoms and effects.

import { atom, useSetRecoilState } from 'recoil';
import { useEffect } from 'react';

const persistedState = atom({
  key: 'persistedState',
  default: JSON.parse(localStorage.getItem('persistedState')) || {},
});

function PersistenceObserver() {
  const setState = useSetRecoilState(persistedState);
  
  useEffect(() => {
    const savedValue = JSON.parse(localStorage.getItem('persistedState'));
    if (savedValue) {
      setState(savedValue);
    }
  }, [setState]);
  
  useEffect(() => {
    return persistedState.subscribe(({nextValue}) => {
      localStorage.setItem('persistedState', JSON.stringify(nextValue));
    });
  }, []);
  
  return null;
}

Dev Tools

Recoil provides built-in developer tools for debugging and inspecting state.

import { useRecoilSnapshot } from 'recoil';

function DebugObserver() {
  const snapshot = useRecoilSnapshot();
  useEffect(() => {
    console.debug('The following atoms were modified:');
    for (const node of snapshot.getNodes_UNSTABLE({isModified: true})) {
      console.debug(node.key, snapshot.getLoadable(node));
    }
  }, [snapshot]);

  return null;
}

Best Practices

  1. Use meaningful keys for atoms and selectors.
  2. Keep atoms small and focused.
  3. Use selectors for derived state.
  4. Avoid redundant state.
  5. Use React's local state for UI-specific state.
  6. Leverage code splitting with dynamic imports for large applications.

Advanced Patterns

Family of Atoms

const todoListState = atomFamily({
  key: 'todoListState',
  default: id => ({
    id,
    text: '',
    isComplete: false,
  }),
});

function TodoItem({id}) {
  const [todoItem, setTodoItem] = useRecoilState(todoListState(id));
  // ...
}

Computed Selectors

const filteredTodoListState = selector({
  key: 'filteredTodoListState',
  get: ({get}) => {
    const filter = get(todoListFilterState);
    const list = get(todoListState);

    switch (filter) {
      case 'Completed':
        return list.filter((item) => item.isComplete);
      case 'Uncompleted':
        return list.filter((item) => !item.isComplete);
      default:
        return list;
    }
  },
});

Async Selectors with Dependencies

const currentUserInfoQuery = selector({
  key: 'currentUserInfoQuery',
  get: async ({get}) => {
    const userId = get(currentUserIDState);
    const response = await fetch(`/api/user/${userId}`);
    return response.json();
  },
});

2024 © All rights reserved - buraxta.com