0Pricing
TypeScript Academy · レッスン

Stateとreducer、Contextの型付け

useStateとuseReducerでコンポーネントのstateを管理し、安全なカスタムフックを備えた型付きContextへstateを持ち上げます

「Stateとreducer、Contextの型付け」はCoddyKit上の無料TypeScript Academyレッスンです。 これはレッスン2/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはTypeScript Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 TypeScript Academyコースには全3レッスンが含まれています。

導入

目標: ローカルな状態(useState)から始め、型付きの reducer(useReducer)へ進み、安全なカスタムフックを備えた型付き Contextへ状態を引き上げます。

  • 状態とアクションを明示的にモデル化する
  • 判別可能な union を優先する
  • Context へのアクセスを保護する

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 の型付け

Action を判別可能な union としてモデル化します。switch は網羅的なままになり、payload は分岐ごとに型付けされます。

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 とフック

Context<T | null> を作成し、null の場合に例外を投げる useX を公開します。利用側はシンプルで、適切に型付けされたままになります。

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>
  )
}

Context と reducer のパターン

コンポーネント間で読み取りと dispatch を分けます。Provider が children をラップし、型付きの state と dispatch を提供します。

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>
  )
}

ベストプラクティス

ベストプラクティス:

  • 状態は最小限に保ち、残りはそこから導出してください。
  • アクションには 判別可能な union を優先してください。
  • カスタムフックで Context へのアクセスを保護してください。

Context の型付けの確認

クイックチェック: React Context を安全に型付けする方法はどれですか。

まとめ

まとめ: useState から始め、useReducer で状態の遷移をモデル化し、カスタムフックで保護した型付き Context に共有状態を引き上げます。

よくある質問

「Stateとreducer、Contextの型付け」レッスンは無料ですか?

はい。「Stateとreducer、Contextの型付け」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、TypeScript Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 TypeScript Academyコースには全3レッスンが含まれています。

「Stateとreducer、Contextの型付け」で何を学びますか?

useStateとuseReducerでコンポーネントのstateを管理し、安全なカスタムフックを備えた型付きContextへstateを持ち上げます ブラウザで直接実行するハンズオンコードでTypeScript Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

TypeScript Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのTypeScript Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/3です。

「Stateとreducer、Contextの型付け」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このTypeScript Academyレッスンでコードを書いて実行できますか?

はい。すべてのTypeScript Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Propsとchildren、イベントハンドラー、refs
  2. Stateとreducer、Contextの型付け
  3. コンポーネントのジェネリクス — 入門
← TypeScript Academyに戻る