Гид по технологиям

Делайте состояние локальным в React — почему и как

5 min read Frontend Обновлено 30 Dec 2025
Локальное состояние в React — зачем и как
Локальное состояние в React — зачем и как

Женщина работает за компьютером Acer

Зачем держать состояние локальным

Если вы часто пишете на React, вероятно, сталкивались с ситуациями, когда состояние хранится в верхнем компоненте и передаётся по цепочке пропсов вниз. Это работает, но приводит к нескольким проблемам:

  • лишним пропсам и «prop drilling»;
  • усложнению родительского компонента ради одного дочернего;
  • трудностям с повторным использованием компонента;
  • потенциальным перерисовкам большого куска дерева при изменении состояния.

Коротко: храните состояние там, где его используют. Это повышает инкапсуляцию, делает логику компонент проще и снижает когнитивную нагрузку.

Простой пример — счётчик

Ниже — обычный пример, где состояние определяется в родительском компоненте и передаётся в Counter.

import {useState} from 'react'
import {Counter} from 'counter'

function App(){
  const [count, setCount] = useState(0)
  return 
}

export default App

А вот реализация самого Counter, которая ожидает внешние count и setCount:

function Counter({count, setCount}) {
  return (
    
{count}
) }

Если никакие другие компоненты не нуждаются в count, то хранение состояния в App — избыточно.

Перенос состояния в дочерний компонент

Гораздо чище, когда Counter сам управляет своим состоянием:

import {useState} from 'react'

function Counter() {
  const [count, setCount] = useState(0)
  return (
    
{count}
) }

Теперь App прост и не передаёт пропсы:

function App(){
  return 
}

Преимущества:

  • два счётчика на странице будут независимы;
  • компонент сам отвечает за своё внутреннее состояние;
  • проще тестировать и переиспользовать компонент.

Формы: когда локальность противоречива

Формы — типичный пример: иногда удобно держать данные формы в родительском компоненте (например, для предварительной загрузки, валидации на уровне страницы или сохранения черновика). Но часто форма — это автономная сущность. Вот исходный пример, где состояние формы лежит в App и прокидывается в LoginForm:

import { useState } from "react";
import { LoginForm } from "./LoginForm";

function App() {
  const [formData, setFormData] = useState({
    email: "",
    password: "",
  });

  function updateFormData(newData) {
    setFormData((prev) => {
      return { ...prev, ...newData };
    });
  }

  function onSubmit() {
    console.log(formData);
  }

  return (
    
  );
}

LoginForm в этом варианте принимает data и updateData:

function LoginForm({ onSubmit, data, updateData }) {
  function handleSubmit(e) {
    e.preventDefault();
    onSubmit();
  }

  return (
    
updateData({ email: e.target.value })} /> updateData({ password: e.target.value })} />
); }

Если форма используется только внутри LoginForm, лучше перенести логику туда. Один из простых способов — заменить управляемые контролы на refs:

import { useRef } from "react";

function LoginForm({ onSubmit }) {
  const emailRef = useRef();
  const passwordRef = useRef();

  function handleSubmit(e) {
    e.preventDefault();
    onSubmit({
      email: emailRef.current.value,
      password: passwordRef.current.value,
    });
  }

  return (
    
); }

В App остаётся только функция onSubmit:

function App() {
  function onSubmit(formData) {
    console.log(formData);
  }

  return ;
}

Плюсы использования useRef в форме:

  • уменьшение количества перерисовок при каждом вводе (нет контролируемых value);
  • проще код, если не нужна немедленная валидация или авто-сохранение;
  • компонент остаётся автономным.

Важно: refs удобны для производительности и простых форм, но если вам нужна немедленная синхронизация поля с UI (валидация «на лету», подсказки) — контролируемые компоненты (useState) всё ещё предпочтительны.

Общие состояния, которые всё же нужно поднимать

Иногда несколько компонентов зависят от одних и тех же данных. Пример:

function TodoContainer() {
  const [todos, setTodos] = useState([])

  return (
    <>
      
      
    
  )
}

В таких случаях состояние должно жить в ближайшем общем предке — здесь это TodoContainer. Перемещение состояния в App создаст излишнюю область видимости и не будет «локальным» к компонентам, которые его используют.

Для больших приложений useState/установка состояния в отдельных компонентах может стать громоздкой: тогда имеет смысл перейти к Context API или управляемому стору (Redux, Zustand, Valtio и т. п.).

Когда локальное состояние — не лучший выбор (примеры)

  • Данные, которые нужны на многих страницах (аутентификация, настройки пользователя) — лучше хранить глобально.
  • Очень глубокое дерево компонентов, где поднимание состояния вызывает prop drilling — используйте Context или локальный стор.
  • Сложная синхронизация с сервером и кэширование — стоит рассмотреть state managers и решения типа React Query.

Альтернативные подходы

  • Context API — для средних сценариев, когда нужно поделиться состоянием между разными ветками дерева.
  • Redux / MobX / Zustand — для сложной логики, сложных трансформаций и предсказуемости.
  • React Query / SWR — для управления асинхронными данными и кэширования.
  • Комбинация: локальное состояние для UI, глобальное для бизнес-данных.

Практическая методология (мини-плейбук)

  1. Начните с локального состояния внутри компонента, который его использует.
  2. Если два или более соседних компонента нуждаются в одном и том же состоянии — поднимите его к ближайшему общему предку.
  3. Если вы оказываете prop drilling через несколько уровней — подумайте о Context для этой части дерева.
  4. Если логика состояния сложна, имеет много побочных эффектов или требует централизованного отладки — выберите стор (Redux/Zustand).
  5. Для форм: используйте контролируемые компоненты (useState) при необходимости валидации в реальном времени; useRef — для простых форм и оптимизации перерисовок.

Чеклист для разработчика

  • Состояние используется только в одном компоненте? — держите локально.
  • Нужны ли данные более чем в одном компоненте, и есть ли общий родитель? — поднимите к ближайшему общему родителю.
  • Происходит ли prop drilling через >2 уровней? — подумайте о Context.
  • Логика состояния сложная и критична? — рассмотрите стор.
  • Нужна ли производительность при больших формах? — попробуйте useRef.

Ментальные модели и эвристики

  • Правило ближайшей ответственности: состояние принадлежит компоненту, который отвечает за данные.
  • Минимальная область видимости: ограждайте состояние так, чтобы по возможности меньше кода зависело от него.
  • Локальность ≠ приватность: локальное состояние инкапсулирует логику, но при необходимости его можно поднять.

Советы по миграции и совместимости

  • Миграция из глобального состояния в локальное: найди компоненты, которые единственные читают/пишут состояние — перенеси туда логику и адаптируй API.
  • При переходе с controlled inputs на refs — проверь валидацию и UX (фокус, подсказки, подсчёт символов).
  • Если используете TypeScript — явно типизируйте состояния и параметры функций onSubmit/handlers.

Факто-бокс

  • useState — самый простой и распространённый хук для локального состояния.
  • useRef — подходит для ссылок на DOM и хранения мутабельных значений без перерисовки.
  • Context — удобен для передачи данных через дерево без пропсов.
  • Редукс/сторы нужны при сложной логике, синхронизации и масштабировании.

Краткое резюме

Делайте состояние как можно локальнее. Это улучшает модульность, облегчает тестирование и уменьшает зависимость между компонентами. Поднимайте состояние только тогда, когда это действительно нужно. Для сложных сценариев применяйте Context или менеджеры состояния.

Важное: нет универсального рецепта — выбирайте подход в зависимости от размеров и требований приложения.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Бесконечная прокрутка на HTML/CSS/JS
Frontend

Бесконечная прокрутка на HTML/CSS/JS

Как измерить потребление электроэнергии ПК — методы и расчёты
Hardware

Как измерить потребление электроэнергии ПК — методы и расчёты

React Native Elements: быстрый старт и темизация
Разработка мобильных приложений

React Native Elements: быстрый старт и темизация

Тёмная тема в Vue — CSS-переменные и LocalStorage
Frontend

Тёмная тема в Vue — CSS-переменные и LocalStorage

Развёртывание React на GitHub Pages
Dev

Развёртывание React на GitHub Pages

Как посмотреть историю дружбы в Facebook
Социальные сети

Как посмотреть историю дружбы в Facebook