Jotai - атомарное состояние

16 июня 2026
Автор

Олег Марков

Введение

Управление состоянием в React-приложениях — одна из тех задач, которая кажется простой на старте и усложняется по мере роста проекта. Context API работает хорошо для небольших случаев, Redux требует значительного количества шаблонного кода, а Zustand предлагает баланс простоты и мощности.

Jotai — это атомарная библиотека управления состоянием для React, вдохновлённая Recoil от Facebook, но с более минималистичным и прагматичным API. Название происходит от японского слова «状態» (jōtai) — «состояние». Библиотека создана командой Poimandres — теми же разработчиками, что написали Zustand и Valtio.

Ключевая идея Jotai — атомы. Атом — это минимальная единица состояния, которую компонент может читать и обновлять. Вместо одного большого глобального стора вы работаете с множеством маленьких, независимых кусочков состояния, которые можно комбинировать.

В этой статье вы познакомитесь с основами Jotai, научитесь создавать атомы разных типов, использовать производные атомы и async-атомы, а также рассмотрим интеграцию с TypeScript и DevTools.

Почему Jotai

Перед тем как переходить к коду, давайте разберёмся, чем Jotai отличается от других популярных решений.

Атомарный подход

Главное отличие Jotai от Redux и Zustand — гранулярность состояния. В Redux и Zustand вы создаёте единый стор (или несколько больших сторов), а компоненты подписываются на его части через селекторы. В Jotai каждый кусочек состояния — это отдельный атом.

// Zustand: один стор с несколькими полями
const useStore = create((set) => ({
  count: 0,
  user: null,
  theme: 'light',
  setCount: (n) => set({ count: n }),
  setUser: (u) => set({ user: u }),
}));

// Jotai: отдельный атом для каждого состояния
const countAtom = atom(0);
const userAtom = atom(null);
const themeAtom = atom('light');

Такой подход обеспечивает более точечные обновления: компонент, подписанный на countAtom, перерендерится только при изменении счётчика, а не при обновлении пользователя.

Минимальный API

API Jotai намеренно сделан маленьким. Основа — всего три концепции: atom, useAtom и Provider. Этого достаточно для большинства задач.

import { atom, useAtom } from 'jotai';

const greetingAtom = atom('Hello, World!');

function Greeting() {
  const [greeting, setGreeting] = useAtom(greetingAtom);
  return <p>{greeting}</p>;
}

Bottom-up модель

В отличие от Redux с его top-down архитектурой, Jotai использует bottom-up подход. Вы не определяете структуру всего глобального состояния заранее — атомы создаются по мере необходимости и автоматически регистрируются в хранилище при первом использовании.

Установка

Установите Jotai через npm или yarn:

npm install jotai
# или
yarn add jotai
# или
pnpm add jotai

Jotai поддерживает React 18+ и React Native. Для React 17 и ниже используйте Jotai версии 1.x.

Базовые концепции

Создание атома

Атом создаётся с помощью функции atom(), которой передаётся начальное значение:

import { atom } from 'jotai';

// Примитивные значения
const countAtom = atom(0);
const nameAtom = atom('');
const isOpenAtom = atom(false);

// Объекты и массивы
const userAtom = atom({ name: '', age: 0 });
const todosAtom = atom([]);

// null и undefined
const selectedAtom = atom(null);

Атомы создаются вне компонентов — как правило, в отдельных модулях. Это позволяет повторно использовать их в любом месте приложения.

Чтение и запись атома

Для работы с атомом в компоненте используется хук useAtom:

import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);

  return (
    <div>
      <p>Счётчик: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(count - 1)}>-</button>
      <button onClick={() => setCount(0)}>Сбросить</button>
    </div>
  );
}

Интерфейс useAtom намеренно похож на useState — это снижает порог входа. Разница в том, что состояние разделяется между всеми компонентами, использующими один и тот же атом.

Разделение чтения и записи

Jotai предоставляет два отдельных хука, если вам нужно только читать или только обновлять атом:

import { useAtomValue, useSetAtom } from 'jotai';

function CounterDisplay() {
  // Только чтение — компонент перерендерится при изменении count
  const count = useAtomValue(countAtom);
  return <p>Текущее значение: {count}</p>;
}

function CounterButtons() {
  // Только запись — компонент НЕ перерендерится при изменении count
  const setCount = useSetAtom(countAtom);

  return (
    <div>
      <button onClick={() => setCount((prev) => prev + 1)}>+</button>
      <button onClick={() => setCount((prev) => prev - 1)}>-</button>
    </div>
  );
}

Это важная оптимизация: useSetAtom не вызывает ре-рендер компонента при изменении значения атома — компонент подписывается только на функцию записи.

Производные атомы

Одна из мощнейших возможностей Jotai — производные атомы (derived atoms). Это атомы, значение которых вычисляется на основе других атомов.

Read-only производные атомы

Производный атом создаётся передачей функции-геттера в atom():

import { atom } from 'jotai';

const priceAtom = atom(100);
const quantityAtom = atom(3);
const discountAtom = atom(0.1); // 10% скидка

// Производный атом — вычисляется автоматически
const totalAtom = atom((get) => {
  const price = get(priceAtom);
  const quantity = get(quantityAtom);
  const discount = get(discountAtom);
  return price * quantity * (1 - discount);
});

function OrderSummary() {
  const price = useAtomValue(priceAtom);
  const quantity = useAtomValue(quantityAtom);
  const total = useAtomValue(totalAtom);

  return (
    <div>
      <p>Цена: {price} ₽</p>
      <p>Количество: {quantity}</p>
      <p>Итого: {total} ₽</p>
    </div>
  );
}

Производный атом автоматически пересчитывается при изменении любого из атомов, от которых он зависит. Пересчёт происходит лениво — только при чтении значения.

Read-write производные атомы

Производный атом может быть доступен и для записи. Для этого в atom() передаётся объект с геттером и сеттером:

const temperatureCAtom = atom(20); // температура в Цельсии

// Двунаправленная конвертация Цельсий ↔ Фаренгейт
const temperatureFAtom = atom(
  (get) => get(temperatureCAtom) * (9 / 5) + 32, // геттер
  (get, set, newValueF) => {
    // сеттер
    const newValueC = ((newValueF - 32) * 5) / 9;
    set(temperatureCAtom, newValueC);
  }
);

function TemperatureConverter() {
  const [celsius, setCelsius] = useAtom(temperatureCAtom);
  const [fahrenheit, setFahrenheit] = useAtom(temperatureFAtom);

  return (
    <div>
      <input
        type="number"
        value={celsius}
        onChange={(e) => setCelsius(Number(e.target.value))}
        placeholder="Цельсий"
      />
      <input
        type="number"
        value={fahrenheit}
        onChange={(e) => setFahrenheit(Number(e.target.value))}
        placeholder="Фаренгейт"
      />
    </div>
  );
}

Здесь temperatureFAtom синхронизирован с temperatureCAtom. Запись в любой из них автоматически обновляет оба.

Async-атомы

Jotai нативно поддерживает асинхронные операции. Вы можете создавать атомы, которые возвращают Promise.

Асинхронный геттер

import { atom, useAtom } from 'jotai';
import { Suspense } from 'react';

const userIdAtom = atom(1);

// Атом, загружающий данные пользователя по ID
const userAtom = atom(async (get) => {
  const userId = get(userIdAtom);
  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
  if (!response.ok) throw new Error('Ошибка загрузки');
  return response.json();
});

function UserProfile() {
  // Компонент приостановится (Suspense), пока данные загружаются
  const [user] = useAtom(userAtom);

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

function App() {
  return (
    <Suspense fallback={<p>Загрузка...</p>}>
      <UserProfile />
    </Suspense>
  );
}

Async-атомы работают через React Suspense — компонент «приостанавливается» до разрешения промиса. Это чистый подход без явной обработки состояний loading/error внутри компонента.

Обработка ошибок

Для обработки ошибок в async-атомах используйте ErrorBoundary:

import { ErrorBoundary } from 'react-error-boundary';

function App() {
  return (
    <ErrorBoundary fallback={<p>Что-то пошло не так</p>}>
      <Suspense fallback={<p>Загрузка...</p>}>
        <UserProfile />
      </Suspense>
    </ErrorBoundary>
  );
}

Атом с ручным обновлением

Если нужно управлять моментом загрузки вручную, используйте атом-триггер:

const refreshCountAtom = atom(0);

const dataAtom = atom(async (get) => {
  get(refreshCountAtom); // подписываемся на триггер
  const response = await fetch('/api/data');
  return response.json();
});

function DataView() {
  const [data] = useAtom(dataAtom);
  const setRefreshCount = useSetAtom(refreshCountAtom);

  const refresh = () => setRefreshCount((c) => c + 1);

  return (
    <div>
      <button onClick={refresh}>Обновить</button>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

Атом-семейства (atomFamily)

Часто нужно создавать похожие атомы для разных сущностей, например, для каждого элемента списка. Для этого в Jotai есть утилита atomFamily из пакета jotai/utils:

import { atomFamily } from 'jotai/utils';

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

// Фабрика атомов — создаёт отдельный атом для каждого ID
const todoAtomFamily = atomFamily(
  (id: number) =>
    atom<Todo>({
      id,
      text: '',
      completed: false,
    })
);

function TodoItem({ id }: { id: number }) {
  const [todo, setTodo] = useAtom(todoAtomFamily(id));

  const toggle = () => setTodo((prev) => ({ ...prev, completed: !prev.completed }));

  return (
    <li
      style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
      onClick={toggle}
    >
      {todo.text}
    </li>
  );
}

atomFamily кэширует созданные атомы — при повторном вызове с тем же аргументом возвращается существующий атом.

Полезные утилиты из jotai/utils

Jotai поставляется с набором готовых утилит для распространённых задач.

atomWithStorage — персистентное состояние

import { atomWithStorage } from 'jotai/utils';

// Состояние автоматически сохраняется в localStorage
const themeAtom = atomWithStorage('theme', 'light');
const languageAtom = atomWithStorage('language', 'ru');

function Settings() {
  const [theme, setTheme] = useAtom(themeAtom);

  return (
    <select value={theme} onChange={(e) => setTheme(e.target.value)}>
      <option value="light">Светлая</option>
      <option value="dark">Тёмная</option>
    </select>
  );
}

При перезагрузке страницы значение восстанавливается из localStorage. Работает также с sessionStorage.

atomWithReducer — Redux-стиль

import { atomWithReducer } from 'jotai/utils';

type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'reset' }
  | { type: 'setAmount'; payload: number };

function counterReducer(state: number, action: Action): number {
  switch (action.type) {
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    case 'reset':
      return 0;
    case 'setAmount':
      return action.payload;
    default:
      return state;
  }
}

const counterAtom = atomWithReducer(0, counterReducer);

function Counter() {
  const [count, dispatch] = useAtom(counterAtom);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset' })}>Сброс</button>
    </div>
  );
}

atomWithDefault — сбрасываемые атомы

import { atomWithDefault, useResetAtom } from 'jotai/utils';

// Атом с поддержкой сброса к начальному значению
const countAtom = atomWithDefault(() => 0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  const resetCount = useResetAtom(countAtom);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount((c) => c + 1)}>+</button>
      <button onClick={resetCount}>Сбросить</button>
    </div>
  );
}

Интеграция с TypeScript

Jotai написан на TypeScript и предоставляет отличный тайп-инференс.

Типизация атомов

import { atom } from 'jotai';

// Тип выводится автоматически
const countAtom = atom(0); // Atom<number>
const nameAtom = atom(''); // Atom<string>

// Явная типизация для null/undefined или сложных типов
interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user';
}

const userAtom = atom<User | null>(null);

// Массивы
const todosAtom = atom<Todo[]>([]);

Типизация производных атомов

// Write-only атом — принимает действие, возвращает void
const addTodoAtom = atom(null, (get, set, newTodo: Todo) => {
  const todos = get(todosAtom);
  set(todosAtom, [...todos, newTodo]);
});

// Async атом с явным типом
const userDataAtom = atom<Promise<User>>(async (get) => {
  const id = get(userIdAtom);
  const response = await fetch(`/api/users/${id}`);
  return response.json() as Promise<User>;
});

Типизация atomFamily

import { atomFamily } from 'jotai/utils';

interface TodoItem {
  id: string;
  text: string;
  completed: boolean;
  createdAt: Date;
}

const todoFamily = atomFamily((id: string) =>
  atom<TodoItem>({
    id,
    text: '',
    completed: false,
    createdAt: new Date(),
  })
);

Provider и scoped-состояние

По умолчанию Jotai использует глобальное хранилище. Но с помощью Provider можно создавать изолированные области состояния:

import { Provider } from 'jotai';

const countAtom = atom(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount((c) => c + 1)}>+</button>
    </div>
  );
}

function App() {
  return (
    <div>
      {/* Каждый Provider — отдельная область состояния */}
      <Provider>
        <Counter /> {/* Свой счётчик */}
      </Provider>
      <Provider>
        <Counter /> {/* Независимый счётчик */}
      </Provider>
    </div>
  );
}

Оба счётчика работают независимо, несмотря на то что используют один countAtom. Это делает компоненты с Jotai изолированными и легко тестируемыми.

Инициализация значений через Provider

import { createStore } from 'jotai';

const myStore = createStore();
myStore.set(countAtom, 42); // задаём начальное значение

function App() {
  return (
    <Provider store={myStore}>
      <Counter />
    </Provider>
  );
}

Jotai DevTools

Jotai поддерживает интеграцию с Redux DevTools Extension для отладки.

Подключение DevTools

npm install jotai-devtools
import { DevTools } from 'jotai-devtools';
import 'jotai-devtools/styles.css';

function App() {
  return (
    <>
      <DevTools /> {/* Панель отладки в браузере */}
      <MainApp />
    </>
  );
}

Именование атомов для отладки

// Добавьте отладочное имя к атому
const countAtom = atom(0);
countAtom.debugLabel = 'counter/count';

const userAtom = atom(null);
userAtom.debugLabel = 'auth/user';

Именованные атомы отображаются с понятными именами в DevTools.

Практический пример: список задач

Соберём полноценный пример — приложение для управления задачами:

import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';

// --- Типы ---
interface Todo {
  id: string;
  text: string;
  completed: boolean;
  createdAt: number;
}

type Filter = 'all' | 'active' | 'completed';

// --- Атомы ---
const todosAtom = atomWithStorage<Todo[]>('todos', []);
const filterAtom = atom<Filter>('all');

// Производный атом — фильтрованный список
const filteredTodosAtom = atom((get) => {
  const todos = get(todosAtom);
  const filter = get(filterAtom);

  switch (filter) {
    case 'active':
      return todos.filter((t) => !t.completed);
    case 'completed':
      return todos.filter((t) => t.completed);
    default:
      return todos;
  }
});

// Производный атом — статистика
const statsAtom = atom((get) => {
  const todos = get(todosAtom);
  return {
    total: todos.length,
    completed: todos.filter((t) => t.completed).length,
    active: todos.filter((t) => !t.completed).length,
  };
});

// --- Действия ---
const addTodoAtom = atom(null, (get, set, text: string) => {
  const newTodo: Todo = {
    id: crypto.randomUUID(),
    text,
    completed: false,
    createdAt: Date.now(),
  };
  set(todosAtom, (prev) => [...prev, newTodo]);
});

const toggleTodoAtom = atom(null, (get, set, id: string) => {
  set(todosAtom, (prev) =>
    prev.map((todo) =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    )
  );
});

const deleteTodoAtom = atom(null, (get, set, id: string) => {
  set(todosAtom, (prev) => prev.filter((todo) => todo.id !== id));
});

const clearCompletedAtom = atom(null, (get, set) => {
  set(todosAtom, (prev) => prev.filter((todo) => !todo.completed));
});

// --- Компоненты ---
function AddTodoForm() {
  const addTodo = useSetAtom(addTodoAtom);
  const [text, setText] = useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (text.trim()) {
      addTodo(text.trim());
      setText('');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Новая задача..."
      />
      <button type="submit">Добавить</button>
    </form>
  );
}

function TodoItem({ todo }: { todo: Todo }) {
  const toggleTodo = useSetAtom(toggleTodoAtom);
  const deleteTodo = useSetAtom(deleteTodoAtom);

  return (
    <li>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={() => toggleTodo(todo.id)}
      />
      <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
        {todo.text}
      </span>
      <button onClick={() => deleteTodo(todo.id)}>✕</button>
    </li>
  );
}

function TodoList() {
  const todos = useAtomValue(filteredTodosAtom);

  if (todos.length === 0) {
    return <p>Нет задач</p>;
  }

  return (
    <ul>
      {todos.map((todo) => (
        <TodoItem key={todo.id} todo={todo} />
      ))}
    </ul>
  );
}

function FilterButtons() {
  const [filter, setFilter] = useAtom(filterAtom);

  return (
    <div>
      {(['all', 'active', 'completed'] as Filter[]).map((f) => (
        <button
          key={f}
          onClick={() => setFilter(f)}
          style={{ fontWeight: filter === f ? 'bold' : 'normal' }}
        >
          {f === 'all' ? 'Все' : f === 'active' ? 'Активные' : 'Выполненные'}
        </button>
      ))}
    </div>
  );
}

function Stats() {
  const stats = useAtomValue(statsAtom);
  const clearCompleted = useSetAtom(clearCompletedAtom);

  return (
    <div>
      <span>Активных: {stats.active}</span>
      <FilterButtons />
      {stats.completed > 0 && (
        <button onClick={clearCompleted}>
          Удалить выполненные ({stats.completed})
        </button>
      )}
    </div>
  );
}

export function TodoApp() {
  return (
    <div>
      <h1>Список задач</h1>
      <AddTodoForm />
      <TodoList />
      <Stats />
    </div>
  );
}

Сравнение Jotai с Recoil и Zustand

Все три библиотеки решают схожие задачи, но с разных сторон.

Jotai vs Recoil

Jotai вдохновлён Recoil, но значительно проще:

Аспект Jotai Recoil
Размер ~3 КБ ~20 КБ
Provider Опциональный Обязательный RecoilRoot
API atom(), useAtom() atom(), selector(), useRecoilState()
Async Нативно через Promise selector с async функцией
Поддержка Активная (Poimandres) Устаревает (Meta)
Стабильность Стабильная v2 Всё ещё экспериментальная

Recoil разграничивает атомы (изменяемое состояние) и селекторы (вычисляемые значения), тогда как в Jotai и то и другое — просто атомы с разной сигнатурой.

Jotai vs Zustand

Аспект Jotai Zustand
Подход Bottom-up (атомы) Top-down (стор)
Гранулярность Высокая (каждый атом — отдельная подписка) Средняя (селекторы из стора)
API Похож на useState Хук с объектом стора
Async Нативно (Suspense) Вручную (loading/error)
DevTools jotai-devtools Redux DevTools
Использование вне React Ограниченное Да (getState, setState)

Zustand лучше подходит для сложного связанного состояния с действиями и middleware. Jotai — для гранулярного состояния, когда важна точная оптимизация ре-рендеров.

Когда использовать Jotai

Jotai хорошо подходит, когда:

  • Много независимых кусочков состояния — атомарная модель обеспечивает чистое разделение
  • Нужна точная оптимизация ре-рендеров — каждый компонент подписывается только на нужные атомы
  • Используете React Suspense — async-атомы работают с ним нативно
  • Хотите минимальный шаблонный код — API проще Redux и даже Zustand
  • Работаете с компонентными библиотекамиProvider изолирует состояние

Возможно, стоит выбрать другое решение, если:

  • У вас сложная бизнес-логика с множеством взаимосвязанных действий — Zustand или Redux Toolkit лучше справятся
  • Нужна работа с состоянием вне React (в сервисах, утилитах) — Zustand удобнее
  • Команда лучше знакома с Redux-паттернами — Redux Toolkit снизит порог входа

Заключение

Jotai — элегантная библиотека для управления состоянием, которая следует принципу «сделай одно, но хорошо». Атомарный подход обеспечивает прекрасную масштабируемость: вы начинаете с простых атомов и наращиваете сложность только там, где это необходимо.

Ключевые преимущества Jotai:

  • Минимальный APIatom и useAtom покрывают 80% случаев
  • Нативная поддержка async через Suspense без лишнего кода
  • Производные атомы — мощный механизм вычисляемых значений
  • Изоляция через Provider — удобно для тестирования и компонентных библиотек
  • Отличный TypeScript — тайп-инференс работает из коробки

Попробуйте Jotai в следующем проекте — возможно, вы обнаружите, что атомарный подход лучше соответствует тому, как вы мыслите о состоянии вашего приложения.

Стрелочка влевоАнимация списков в ReactБесконечная прокруткаСтрелочка вправо

Постройте личный план изучения React до уровня Middle — бесплатно!

React — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React

Zustand — управление состоянием в ReactZod - валидация с TypeScriptYup - валидация схемXState - конечные автоматыТемизация в ReactТестирование хуковTailwind CSS с ReactSWR - библиотека для запросовStyled Components — стилизация через JSStorybook - документация компонентовSnapshots тестированиеRTK Query - работа с APIRedux Toolkit - современный ReduxRecoil — библиотека управления состоянием от FacebookВиртуализация списков с react-window: как отображать тысячи элементов без лаговReact Toastify - уведомления в ReactReact Testing LibraryСоздание таблиц в React гайд по react-tableReact Spring - анимацииРабота с формами и селектами в ReactReact Query (TanStack Query) - работа с серверомПлагины в React что это и как их использоватьReact PDF - работа с PDF файламиОбзор популярных библиотек для ReactReact Icons - библиотека иконок для ReactReact Hook Form — валидация форм в ReactReact Dropzone — загрузка файловПодключение Bootstrap к React-приложениюReact Beautiful DnD - перетаскивание элементовАнимация при монтировании компонентов в ReactМокирование APIMobX — реактивное управление состоянием в ReactМикрофронтенды с React (micro-frontends)Загрузка и индикаторыАнимация списков в ReactJotai - атомарное состояниеБесконечная прокруткаFramer Motion - библиотека анимацийEmotion — библиотека CSS-in-JSДинамические стили в ReactE2E тестирование с CypressCSSTransition - переходыCSS-in-JS — плюсы и минусыКонтекст vs Redux — когда что использоватьИспользование Chart.js в ReactAxios с ReactТестирование асинхронных компонентовОбработка ошибок API
Uncontrolled Components: когда DOM управляет даннымиБезопасность в React: защита от XSS, CSRF и утечек данныхRender Props: гибкое управление рендерингом в ReactРефакторинг React-кода: техники и лучшие практикиПрофилирование React: как найти и устранить узкие местаЧастичное применение: как создавать компоненты без лишнего кодаИменование компонентов в React: соглашения и лучшие практикиЛенивая загрузка: как ускорить React-приложение в разыHOC в React: мастерство композиции компонентовuseMemo: как спасти производительность от тяжелых вычисленийError Boundaries: создаем надежные React-приложенияКонтролируемые компоненты в React: полный контроль над формамиCompound Components в React: создаем гибкие компоненты с мощным APIДокументирование компонентов в React: Storybook, JSDoc и READMEКомпозиция компонентов в React: строим гибкие интерфейсыКомментирование кода в React: когда и как писать комментарииCode Splitting в React: как уменьшить бандл и ускорить загрузку приложенияАсинхронные компоненты в React: новый стандарт работы с даннымиДоступность (a11y) в React: ARIA, семантика и клавиатурная навигация
useState в React что это и как использоватьuseTransition - плавные переходы между состояниямиuseSyncExternalStore — работа с внешними сторамиuseRef в React — создание ссылок на DOM и значенияuseOptimistic — оптимистичные обновления UIuseLayoutEffect в React — эффект до отрисовкиuseInsertionEffect — внедрение стилей до мутаций DOMuseImperativeHandle в React — настройка ref дочернего компонентаuseId — генерация уникальных идентификаторовuseFormStatus - отслеживание статуса отправки формыuseDeferredValue — отложенное обновление состоянияuseDebugValue — отладка кастомных хуковuseCallback в React — мемоизация функцийuseReducer — альтернатива useState для сложной логикиuseMemo в React: как и когда оптимизировать тяжелые вычисленияuseEffect в React что это и как использоватьuseContext — работа с контекстом в ReactuseCallback в React — мемоизация функций и оптимизация ре-рендеровuseActionState в React 19Оптимизация рендеринга в React: от теории к глубокой практикеЧто такое useRef и как его применять в ReactКак и зачем использовать React HooksУправление состоянием в React через ContextКак предотвратить лишние ре-рендеры в React: полное руководствоuseMemo vs useCallback: подробное руководство по мемоизации в ReactПравила хуков — правила использованияuseEffect vs useLayoutEffect: в чём разница и какой хук выбрать?Кастомные хуки в React — создание собственных хуковuseState продвинутое использование в React
Transition API — плавные обновления интерфейса в ReactReact Suspense — приостановка рендераStrictMode в React — как находить ошибки на этапе разработкиСерверные компоненты React (RSC) — подробный разбор и практикаКак работает рендеринг в ReactЧто такое props в React и как их правильно использоватьКак работает JSX связка React и HTMLЧто такое React.js и как его использоватьКак использовать элементы в ReactКак использовать React DOM в проектеЧто такое компоненты в React и как их применятьРабота с children в ReactПорталы в React: рендер компонентов вне иерархии DOMFragment в React: группировка элементов без лишних узлов DOMCSS Modules в ReactConcurrent Mode — конкурентный режим в React
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React и Redux Toolkit

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий