6-2. Stateの型を定義する

useStateに型を指定する方法を学びます

React - 第4章: TypeScript + React

課題:Stateの型を定義しよう

useStateにも型を指定できます。特にオブジェクトを扱う時は、型を明示すると安全です。 Todoアイテムの型を定義して、Todoリストを作ってみましょう。

やること

  1. 1.Todo インターフェースを定義する(id: number, text: string, completed: boolean)
  2. 2.useStateで Todo[] 型の配列を管理する
  3. 3.Todoリストを表示する
  4. 4.「完了」ボタンでcompletedを切り替える

Stateの型定義

オブジェクトの型を定義します:

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

useStateで型を指定します:

const [todos, setTodos] = useState<Todo[]>([
  { id: 1, text: "買い物に行く", completed: false },
  { id: 2, text: "レポートを書く", completed: true },
]);

useState<Todo[]> のように、 ジェネリクスで型を指定します。

ポイント:複雑なオブジェクトを扱う時は、必ず型を定義しましょう。typoやプロパティ名の間違いを防げます。

期待される出力

Todo リスト

□ 買い物に行く [完了]
☑ レポートを書く [未完了]
□ メールを返信する [完了]
import { useState } from 'react';

// ここにTodoインターフェースを定義してください
// id: number
// text: string
// completed: boolean


function App() {
  // ここのuseStateに型を指定してください
  const [todos, setTodos] = useState([
    { id: 1, text: "買い物に行く", completed: false },
    { id: 2, text: "レポートを書く", completed: true },
    { id: 3, text: "メールを返信する", completed: false },
  ]);
  
  const toggleTodo = (id: number) => {
    setTodos(todos.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  };
  
  return (
    <div style={{ padding: '20px', maxWidth: '500px', margin: '0 auto' }}>
      <h2>Todo リスト</h2>
      
      <div style={{ marginTop: '20px' }}>
        {todos.map(todo => (
          <div 
            key={todo.id}
            style={{ 
              padding: '15px',
              marginBottom: '10px',
              backgroundColor: todo.completed ? '#d4edda' : '#f8f9fa',
              border: `1px solid ${todo.completed ? '#c3e6cb' : '#dee2e6'}`,
              borderRadius: '4px',
              display: 'flex',
              justifyContent: 'space-between',
              alignItems: 'center'
            }}
          >
            <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
              <span style={{ fontSize: '20px' }}>
                {todo.completed ? '☑' : '□'}
              </span>
              <span style={{ 
                textDecoration: todo.completed ? 'line-through' : 'none',
                color: todo.completed ? '#666' : '#000'
              }}>
                {todo.text}
              </span>
            </div>
            
            <button 
              onClick={() => toggleTodo(todo.id)}
              style={{ 
                padding: '5px 15px',
                fontSize: '14px',
                backgroundColor: todo.completed ? '#ffc107' : '#28a745',
                color: 'white',
                border: 'none',
                borderRadius: '4px',
                cursor: 'pointer'
              }}
            >
              {todo.completed ? '未完了' : '完了'}
            </button>
          </div>
        ))}
      </div>
    </div>
  );
}

export default App;