← Назад к вопросам

Как прокидываешь состояние по дочерним компонентам?

2.0 Middle🔥 191 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы передачи состояния дочерним компонентам

В React существует несколько паттернов для передачи состояния и данных вниз по иерархии компонентов. Выбор метода зависит от сложности приложения и структуры компонентов.

Props Drilling (Прямая передача через props)

Самый простой способ - передача данных через props на каждом уровне:

function Parent() {
  const [user, setUser] = useState({ name: 'John', age: 30 });

  return (
    <div>
      <Level1 user={user} setUser={setUser} />
    </div>
  );
}

function Level1({ user, setUser }) {
  return (
    <div>
      <Level2 user={user} setUser={setUser} />
    </div>
  );
}

function Level2({ user, setUser }) {
  return (
    <div>
      <Level3 user={user} setUser={setUser} />
    </div>
  );
}

function Level3({ user, setUser }) {
  return (
    <div>
      <p>Name: {user.name}</p>
      <button onClick={() => setUser({ ...user, name: 'Jane' })}>
        Change Name
      </button>
    </div>
  );
}

Context API (Более оптимально для глубокой иерархии)

Когда компонентов много, Context API избавляет от prop drilling:

const UserContext = React.createContext();

export function UserProvider({ children }) {
  const [user, setUser] = useState({ name: 'John', age: 30 });

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}

export function useUser() {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error('useUser must be used within UserProvider');
  }
  return context;
}

function DeepComponent() {
  const { user, setUser } = useUser();
  return (
    <div>
      <p>Name: {user.name}</p>
      <button onClick={() => setUser({ ...user, name: 'Jane' })}>
        Change Name
      </button>
    </div>
  );
}

function App() {
  return (
    <UserProvider>
      <Level1 />
    </UserProvider>
  );
}

useState + useCallback для оптимизации

Чтобы избежать лишних перерендеров при передаче обработчиков:

function Parent() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  const decrement = useCallback(() => {
    setCount(prev => prev - 1);
  }, []);

  return (
    <div>
      <Counter 
        count={count} 
        onIncrement={increment} 
        onDecrement={decrement} 
      />
      <ChildComponent count={count} />
    </div>
  );
}

function Counter({ count, onIncrement, onDecrement }) {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={onIncrement}>+</button>
      <button onClick={onDecrement}>-</button>
    </div>
  );
}

Compound Components Pattern

Паттерн для создания связанных компонентов с общим состоянием:

function Form() {
  const [formData, setFormData] = useState({
    name: '',
    email: ''
  });

  return (
    <FormProvider value={{ formData, setFormData }}>
      <FormName />
      <FormEmail />
      <FormSubmit />
    </FormProvider>
  );
}

function FormName() {
  const { formData, setFormData } = useFormContext();
  return (
    <input
      value={formData.name}
      onChange={(e) => setFormData({ ...formData, name: e.target.value })}
      placeholder="Name"
    />
  );
}

function FormEmail() {
  const { formData, setFormData } = useFormContext();
  return (
    <input
      value={formData.email}
      onChange={(e) => setFormData({ ...formData, email: e.target.value })}
      placeholder="Email"
    />
  );
}
Как прокидываешь состояние по дочерним компонентам? | PrepBro