0Pricing
TypeScript Academy · Урок

Состояние и редьюсеры; типизация Context

Управляйте состоянием компонента с помощью useState и useReducer, а затем выносите состояние в типизированный Context с безопасным пользовательским хуком.

«Состояние и редьюсеры; типизация Context» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 2 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 3 уроков всего.

Введение

Цель: начните с локального состояния (useState), перейдите к типизированному редуктору (useReducer), а затем поднимите состояние в типизированный контекст с безопасным пользовательским хуком.

  • Явно моделируйте состояние и действия
  • Предпочитайте размеченные объединения
  • Защищайте доступ к контексту

Типизированный useState

Аннотируйте состояние конкретным типом (например, number). Используйте форму обновления, чтобы избежать устаревших замыканий при быстрых обновлениях.

import React, { useState } from "react"

export default function Counter() {
  const [count, setCount] = useState<number>(0)

  const inc = () => setCount(c => c + 1)
  const dec = () => setCount(c => c - 1)

  return (
    <div style={{ display: "grid", gap: 8 }}>
      <div>Count: {count}</div>
      <div style={{ display: "flex", gap: 8 }}>
        <button onClick={inc}>+1</button>
        <button onClick={dec}>-1</button>
      </div>
    </div>
  )
}

Типизированный useReducer

Моделируйте действие как размеченное объединение. Конструкция switch остаётся исчерпывающей, а полезные данные типизируются для каждой ветки.

import React, { useReducer } from "react"

type State = { count: number }

type Action =
  | { type: "inc" }
  | { type: "add"; by: number }
  | { type: "reset" }

function reducer(s: State, a: Action): State {
  switch (a.type) {
    case "inc": return { count: s.count + 1 }
    case "add": return { count: s.count + a.by }
    case "reset": return { count: 0 }
  }
}

export default function App() {
  const [state, dispatch] = useReducer(reducer, { count: 0 })
  return (
    <div style={{ display: "grid", gap: 8 }}>
      <div>Count: {state.count}</div>
      <button onClick={() => dispatch({ type: "inc" })}>+1</button>
      <button onClick={() => dispatch({ type: "add", by: 5 })}>+5</button>
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
    </div>
  )
}

Контекст и хук

Создайте Context<T | null>, а затем предоставьте хук useX, который выдаёт ошибку при значении null. Код потребителей остаётся простым и хорошо типизированным.

import React, { createContext, useContext, useReducer } from "react"

type State = { count: number }

type Action = { type: "inc" } | { type: "add"; by: number } | { type: "reset" }

function reducer(s: State, a: Action): State {
  switch (a.type) {
    case "inc": return { count: s.count + 1 }
    case "add": return { count: s.count + a.by }
    case "reset": return { count: 0 }
  }
}

type Ctx = { state: State; dispatch: React.Dispatch<Action> }

const CounterCtx = createContext<Ctx | null>(null)

export function useCounter() {
  const ctx = useContext(CounterCtx)
  if (!ctx) throw new Error("useCounter must be used within CounterProvider")
  return ctx
}

export function CounterProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(reducer, { count: 0 })
  return <CounterCtx.Provider value={{ state, dispatch }}>{children}</CounterCtx.Provider>
}

export default function App() {
  return (
    <CounterProvider>
      <Inner />
    </CounterProvider>
  )
}

function Inner() {
  const { state, dispatch } = useCounter()
  return (
    <div style={{ display: "grid", gap: 8 }}>
      <div>Count: {state.count}</div>
      <button onClick={() => dispatch({ type: "inc" })}>+1</button>
    </div>
  )
}

Контекст и шаблон с редуктором

Разделите чтение и отправку действий между компонентами. Провайдер оборачивает дочерние элементы и предоставляет типизированные состояние и отправку действий.

import React from "react"
import { CounterProvider, useCounter } from "./ctx"

function Controls() {
  const { dispatch } = useCounter()
  return (
    <div style={{ display: "flex", gap: 8 }}>
      <button onClick={() => dispatch({ type: "inc" })}>+1</button>
      <button onClick={() => dispatch({ type: "add", by: 10 })}>+10</button>
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
    </div>
  )
}

function View() {
  const { state } = useCounter()
  return <div>Count: {state.count}</div>
}

export default function App() {
  return (
    <CounterProvider>
      <div style={{ display: "grid", gap: 8 }}>
        <View />
        <Controls />
      </div>
    </CounterProvider>
  )
}

Лучшие практики

Рекомендации:

  • Делайте состояние минимальным, а остальное выводите из него.
  • Предпочитайте размеченные объединения для действий.
  • Защищайте доступ к контексту с помощью пользовательского хука.

Проверка типизации контекста

Быстрая проверка: как безопасно типизировать контекст React?

Повторение

Повторение: начните с useState, моделируйте переходы с помощью useReducer и поднимите общее состояние в типизированный контекст, защищённый пользовательским хуком.

Часто задаваемые вопросы

Урок «Состояние и редьюсеры; типизация Context» бесплатный?

Да — полный текст урока «Состояние и редьюсеры; типизация Context» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 3 уроков всего.

Чему я научусь в уроке «Состояние и редьюсеры; типизация Context»?

Управляйте состоянием компонента с помощью useState и useReducer, а затем выносите состояние в типизированный Context с безопасным пользовательским хуком. Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать TypeScript Academy?

Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 3.

Сколько времени занимает урок «Состояние и редьюсеры; типизация Context»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке TypeScript Academy?

Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Свойства и дочерние элементы; обработчики событий; ссылки
  2. Состояние и редьюсеры; типизация Context
  3. Обобщения компонентов — введение
← Назад к TypeScript Academy