CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-recoil

Recoil is an experimental state management framework for React applications that provides atoms and selectors for fine-grained reactivity.

Pending
Overview
Eval results
Files

state-definition.mddocs/

State Definition

Core functions for defining atoms and selectors that form the foundation of Recoil's state graph. Atoms represent units of state, while selectors represent derived state or computations.

Capabilities

Atom Definition

Creates a unit of state that components can read from and write to.

/**
 * Creates an atom, which represents a piece of writeable state
 */
function atom<T>(options: AtomOptions<T>): RecoilState<T>;

type AtomOptions<T> = {
  /** Unique string identifying this atom */
  key: string;
  /** Default value for the atom */
  default?: T | RecoilValue<T> | Promise<T> | Loadable<T> | WrappedValue<T>;
  /** Array of effects to run when atom is first used */
  effects?: ReadonlyArray<AtomEffect<T>>;
  /** Allow direct mutation of atom values (use with caution) */
  dangerouslyAllowMutability?: boolean;
};

Usage Examples:

import { atom } from 'recoil';

// Simple atom with primitive default
const countState = atom({
  key: 'countState',
  default: 0,
});

// Atom with object default
const userState = atom({
  key: 'userState',
  default: {
    id: null,
    name: '',
    email: '',
  },
});

// Atom with async default
const userProfileState = atom({
  key: 'userProfileState',
  default: fetch('/api/user').then(res => res.json()),
});

// Atom with effects
const persistedState = atom({
  key: 'persistedState',
  default: '',
  effects: [
    ({setSelf, onSet}) => {
      // Initialize from localStorage
      const saved = localStorage.getItem('persistedState');
      if (saved != null) {
        setSelf(JSON.parse(saved));
      }
      
      // Save to localStorage on changes
      onSet((newValue) => {
        localStorage.setItem('persistedState', JSON.stringify(newValue));
      });
    },
  ],
});

Atom Value Wrapping

Utility for preventing automatic unwrapping of values in atoms and selectors.

namespace atom {
  /**
   * Wraps a value to prevent unwrapping by Recoil
   */
  function value<T>(value: T): WrappedValue<T>;
}

interface WrappedValue<T> {
  readonly [WrappedValue_OPAQUE]: true;
}

Usage Examples:

import { atom } from 'recoil';

// Prevent unwrapping of promises
const promiseState = atom({
  key: 'promiseState',
  default: atom.value(Promise.resolve('value')), // Promise won't be unwrapped
});

// Prevent unwrapping of Recoil values
const wrappedAtomState = atom({
  key: 'wrappedAtomState',  
  default: atom.value(someOtherAtom), // Atom won't be read
});

Selector Definition

Creates derived state that can depend on atoms or other selectors.

/**
 * Creates a selector which represents derived state
 */
function selector<T>(options: ReadWriteSelectorOptions<T>): RecoilState<T>;
function selector<T>(options: ReadOnlySelectorOptions<T>): RecoilValueReadOnly<T>;

interface ReadOnlySelectorOptions<T> {
  /** Unique string identifying this selector */
  key: string;
  /** Function that computes the selector's value */
  get: (opts: {
    get: GetRecoilValue;
    getCallback: GetCallback;
  }) => T | RecoilValue<T> | Promise<T> | Loadable<T> | WrappedValue<T>;
  /** Cache policy for this selector */
  cachePolicy_UNSTABLE?: CachePolicyWithoutEquality;
  /** Allow direct mutation of selector values (use with caution) */
  dangerouslyAllowMutability?: boolean;
}

interface ReadWriteSelectorOptions<T> extends ReadOnlySelectorOptions<T> {
  /** Function that handles setting the selector's value */
  set: (opts: {
    set: SetRecoilState;
    get: GetRecoilValue;
    reset: ResetRecoilState;
  }, newValue: T | DefaultValue) => void;
}

Usage Examples:

import { atom, selector, DefaultValue } from 'recoil';

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

// Read-only selector
const charCountState = selector({
  key: 'charCountState',
  get: ({get}) => {
    const text = get(textState);
    return text.length;
  },
});

// Async selector
const userNameState = selector({
  key: 'userNameState',
  get: async ({get}) => {
    const userID = get(currentUserIDState);
    const response = await fetch(`/api/users/${userID}`);
    return response.json();
  },
});

// Read-write selector
const tempFahrenheit = selector({
  key: 'tempFahrenheit',
  get: ({get}) => {
    const tempCelsius = get(tempCelsiusState);
    return (tempCelsius * 9) / 5 + 32;
  },
  set: ({set}, newValue) => {
    const tempCelsius = ((newValue as number) - 32) * 5 / 9;
    set(tempCelsiusState, tempCelsius);
  },
});

// Selector with error handling
const safeDataState = selector({
  key: 'safeDataState',
  get: async ({get}) => {
    try {
      const data = await get(asyncDataState);
      return data;
    } catch (error) {
      return { error: error.message };
    }
  },
});

Selector Value Wrapping

Utility for preventing automatic unwrapping of values in selectors.

namespace selector {
  /**
   * Wraps a value to prevent unwrapping by Recoil
   */
  function value<T>(value: T): WrappedValue<T>;
}

Convenience Selectors

Pre-built selectors for common use cases.

/**
 * Returns a selector that always has a constant value
 */
function constSelector<T extends SerializableParam>(constant: T): RecoilValueReadOnly<T>;

/**
 * Returns a selector which is always in the provided error state
 */
function errorSelector(message: string): RecoilValueReadOnly<never>;

/**
 * Casts a selector to be a read-only selector
 */
function readOnlySelector<T>(atom: RecoilValue<T>): RecoilValueReadOnly<T>;

Usage Examples:

import { constSelector, errorSelector, readOnlySelector } from 'recoil';

// Constant selector
const appVersionState = constSelector('1.0.0');

// Error selector for testing
const errorState = errorSelector('This always errors');

// Read-only wrapper
const readOnlyUserState = readOnlySelector(userState);

Atom Effects

System for adding side effects to atoms when they are first used.

type AtomEffect<T> = (param: {
  node: RecoilState<T>;
  storeID: StoreID;
  trigger: 'set' | 'get';
  setSelf: (param: T | DefaultValue | Promise<T | DefaultValue> | WrappedValue<T> | 
    ((param: T | DefaultValue) => T | DefaultValue | WrappedValue<T>)) => void;
  resetSelf: () => void;
  onSet: (param: (newValue: T, oldValue: T | DefaultValue, isReset: boolean) => void) => void;
  getPromise: <S>(recoilValue: RecoilValue<S>) => Promise<S>;
  getLoadable: <S>(recoilValue: RecoilValue<S>) => Loadable<S>;
  getInfo_UNSTABLE: <S>(recoilValue: RecoilValue<S>) => RecoilStateInfo<S>;
}) => void | (() => void);

Usage Examples:

import { atom } from 'recoil';

// Persistence effect
const localStorageEffect = (key: string) => ({setSelf, onSet}) => {
  const savedValue = localStorage.getItem(key);
  if (savedValue != null) {
    setSelf(JSON.parse(savedValue));
  }

  onSet((newValue, _, isReset) => {
    isReset
      ? localStorage.removeItem(key)
      : localStorage.setItem(key, JSON.stringify(newValue));
  });
};

// Logging effect
const loggingEffect = ({onSet, node}) => {
  onSet((newValue, oldValue) => {
    console.log(`${node.key} changed from`, oldValue, 'to', newValue);
  });
};

// Atom with effects
const trackedState = atom({
  key: 'trackedState',
  default: '',
  effects: [
    localStorageEffect('tracked-state'),
    loggingEffect,
  ],
});

Cache Policies

Configuration for selector result caching and eviction.

type EvictionPolicy = 'lru' | 'keep-all' | 'most-recent';

type CachePolicyWithoutEquality = 
  | {eviction: 'lru', maxSize: number} 
  | {eviction: 'keep-all'} 
  | {eviction: 'most-recent'};

Install with Tessl CLI

npx tessl i tessl/npm-recoil

docs

advanced-hooks.md

concurrency-helpers.md

core-hooks.md

family-patterns.md

index.md

loadable-system.md

memory-management.md

root-provider.md

state-definition.md

tile.json