useState

目次
  • 解説
  • 1. 準備
  • 2. 正解・不正解を判定する
  • 3. 状態を更新してみよう
問題に挑戦!
進捗を変更する




解説

1. 準備


アプリに必要な各要素を表示することができたので、次は正解・不正解を判定する処理を追加しましょう。

従来のJavaScriptであればボタン要素を監視して、結果に応じてclassの付与やvalueの書き換えなどを行い要素を更新していました。

Reactでは、関数コンポーネントでそれらを効率的に行うために、「React Hooksフック)」という機能があります。フックにはいくつか種類があり、このアプリでは状態を管理することができる「useState」と、外部通信要素の更新などの処理(副作用)を実行するための「useEffect」の2つの一般的なフックを使用します。

2. 正解・不正解を判定する


選んだカラーコードが正解か不正解かを判定するコードを追記しましょう。

手順1

(1)以下のコードを参考に、「ColorQuiz」コンポーネント内にコードを追記してください。
export const ColorQuiz = (): JSX.Element => {
  // 追加
  const answer = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
    const selectedColor: string = e.currentTarget.value;
    if (selectedColor === correctColor) {
      console.log('正解');
    } else {
      console.log('不正解');
    }
  };

  return (
(2)<button>要素にonClick属性を追加してください。
<button value={color} key={color} type="button" onClick={answer}>
  {color}
</button>

ボタンをクリックするたびにanswer関数が呼び出され、正解のカラーコード(correctColor)と一致するか判定をした後、その結果をコンソールに出力しています。

Preview」に表示されている回答ボタンをクリックして、画面右下の「Console」に結果が表示されるか確認しましょう。

3. 状態を更新してみよう


判定結果の状態を管理して、コンソールではなく「Preview」に表示してみましょう。

手順2

(1)下記のコードを参考に、コードを追記、修正してください。
const [result, setResult] = useState<string>(''); // 追加

const answer = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
  const selectedColor: string = e.currentTarget.value;
  if (selectedColor === correctColor) {
    setResult('正解'); // 変更
  } else {
    setResult('不正解'); // 変更
  }
};
<div>
  {questionColors.map((color) => (
    <button value={color} key={color} type="button" onClick={answer}>
      {color}
    </button>
  ))}
</div>
{/* 追加 */}
<h4>{result}</h4>
(2)以下の画像を参考に、エラーがあることを示す赤い波線にカーソルを合わせてツールチップを表示させ、「Quick Fix > Add import from 'react'」をクリックして「react」ライブラリから「useState」をインポートしてください。

※Quick Fixが表示されない場合は以下のコードをファイル内最上部に追記してください。

import { useState } from 'react';

useStateを使用することで、変数の値を更新するだけで判定結果の表示を切り替えることができました。

構文:useState
import { useState } from 'react';

const [状態変数, 状態変数を更新する関数] = useState<型>(初期値);

例) クリックした回数を表示するカウンター

import { useState } from 'react';

export const Counter = (): JSX.Element => {
  const [count, setCount] = useState<number>(0);

  return (
    <>
      <p>{count}回クリック</p>
      <button onClick={() => setCount(count + 1)}>追加</button>
    </>
  );
}

ボタンをクリックするたびに、表示されている数字が1ずつ増えていくサンプルです。

解説

状態変数と再レンダリング

const [count, setCount] = useState<number>(0);

useStateは関数型コンポーネント内直下で、上記のルールで宣言して使用します。

配列の一つ目の値(count)が状態変数となり、状態が更新されるたびに変更の差分を検出し、状態変数を参照している箇所の再レンダリングを行います。(「○回クリック」が再レンダリングされる)

更新関数

setCount(10); // countが10に更新される
setCount(count + 1); // countがインクリメントされる(+1される)

配列の二つ目の値(setCount)である更新関数が、状態変数を更新する唯一の手段になります。一般的に、状態変数の接頭辞に「set」をつけた名前で宣言し、引数に変更後の値を指定して呼び出すことで状態変数を更新する(再レンダリングする)ことができます。

型と初期値

const [count, setCount] = useState<number>(0);

TypeScriptを使用している場合、を明示的に指定して型チェックをすることができます。

const [count, setCount] = useState<number>('0回'); // number型にstringを代入しているのでエラーが表示される

また初期値を設定することも可能です。TypeScriptではuseStateの初期値が指定されていない場合、状態変数の値はundefinedとなります。そのため、初期値を設定しない場合は、型を指定していたとしてもundefinedを許容する型になってしまいます。(「number | undefined」等)
undefinedを許容しない、より型安全なコードにする場合は初期値を設定するようにします。

問題

実践問題


各問に答えてください。

const answer = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
  const selectedColor: string = e.currentTarget.value;
  if (selectedColor === correctColor) {
    setResult('正解');
  } else {
    setResult('不正解');
  }
  // (問1) 結果表示の2秒後に、結果を非表示にしてください。
};

解答


(問1)解答を表示
setTimeout(() => {
  setResult('');
}, 2000);

ソース全文を表示
import { useState } from 'react';

// 回答の選択肢
const questionColors: string[] = ['#e6e6fa', '#e0ffff', '#fff0f5'];
// 問題の背景色(正解の色)
const correctColor: string = questionColors[Math.floor(Math.random() * 3)];

export const ColorQuiz = (): JSX.Element => {
  const [result, setResult] = useState<string>('');

  const answer = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
    const selectedColor: string = e.currentTarget.value;
    if (selectedColor === correctColor) {
      setResult('正解');
    } else {
      setResult('不正解');
    }
    setTimeout(() => {
      setResult('');
    }, 2000);
  };

  return (
    <>
      <h1>カラークイズ</h1>
      <div
        style={{
          width: '150px',
          height: '150px',
          backgroundColor: correctColor
        }}
      ></div>
      <div>
        {questionColors.map((color) => (
          <button value={color} key={color} type="button" onClick={answer}>
            {color}
          </button>
        ))}
      </div>
      <h4>{result}</h4>
    </>
  );
};