useEffect

目次
  • 解説
  • 1. レンダリング後に処理を行う
  • 実践問題
  • 2. 問題をランダムに生成しよう
  • 実践問題
進捗を変更する




解説

1. レンダリング後に処理を行う


回答の結果を表示することができました。次は正解した後に新規の問題を表示するようにコードを追加しましょう。

手順1

(1)正解の色を変更できるようにするために、以下のコードを参考に「ColorQuiz.tsx」ファイル内の「correctColor」をuseState(状態変数)に修正してください。
const [correctColor, setCorrectColor] = useState<string>(questionColors[Math.floor(Math.random() * 3)]);
(2)正解したら「correctColor」を変更して新規の問題を作成するために、「ColorQuiz」コンポーネント内に以下のコードを追記してください。
useEffect(() => {
  if (result === '正解') {
    setCorrectColor(questionColors[Math.floor(Math.random() * 3)]);
  }
}, [result]);
(3)以下の画像を参考に、「react」ライブラリから「useEffect」をインポートしてください。

※Quick Fixが表示されない場合は以下のコードを参考にuseEffectをimportしてください。

import { useEffect, useState } from 'react';

useEffectを使用することで、正解の色をランダムに設定し直して、次の問題を表示できるようになりました。

※続けて同じ色が表示されることもあります。

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

useEffect(() => {
  // 実行したい副作用
}, [依存配列]);

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

import { useEffect, useState } from 'react';

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

  useEffect(() => {
    console.log('countが変更されました!');
  }, [count]);

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

ボタンをクリックして、「count」が更新されるたびにuseEffect内でconsole.logが実行され、「Console」に出力されるサンプルです。

解説

コールバック関数

useEffectの1つ目の引数として渡されるコールバック関数(() => {})には実行したい副作用の処理を記述します。

依存配列

useEffectの2つ目の引数として渡される依存配列([count])は、上記コールバック関数の実行をトリガーするための依存関係を表し、この依存関係に含まれる値が変更された場合にのみ、コールバック関数が再実行されます。通常の変数ではなく状態変数を指定する必要があり、指定方法には以下のパターンがあります。

空の配列

useEffect(() => {
  // 実行したい副作用
}, []);

依存配列に空の配列を指定した場合、コールバック関数はコンポーネントの初回のマウント(DOMに挿入される)時に一度だけ実行されます。

特定の値の配列

useEffect(() => {
  // 実行したい副作用
}, [value]);

依存配列に状態変数を指定した場合、その変数の値が変更された場合にのみコールバック関数が実行されます。上記のサンプルでは依存配列に状態変数「count」が指定されているため、「count」の値が変更されるたびにコールバック関数が実行されます。

複数の値の配列

useEffect(() => {
  // 実行したい副作用
}, [value1, value2]);

依存配列には複数の状態変数を指定することができ、指定したいずれかの変数の値が変更された場合にコールバック関数が実行されます。

指定なし

useEffect(() => {
  // 実行したい副作用
});

依存配列が指定されていない場合、コンポーネントの初回のマウント時と、コンポーネントの再レンダリング時に毎回実行されます。

実践問題


(1)useEffect内の、正解の色を再代入する処理を新たな関数「newGame」として「ColorQuiz」コンポーネント内に定義し、useEffect内ではnewGame関数を呼び出すように変更してください。
(2)correctColor」の初期値を「''(空文字)」に変更し、マウント時に一回だけ実行されるuseEffectを作成し、その中でnewGame関数を呼び出して初回の正解の色を設定するように変更してください。

解答


(問1)解答を表示
useEffect(() => {
  if (result === '正解') {
    newGame();
  }
}, [result]);

const newGame = () => {
  setCorrectColor(questionColors[Math.floor(Math.random() * 3)]);
};

(問2)解答を表示
const [correctColor, setCorrectColor] = useState<string>('');
  .
  .
  .
useEffect(() => {
  newGame();
}, []);

2. 問題をランダムに生成しよう


ここまでのコードでは、あらかじめ指定した3つのカラーコードからしか回答できないので、問題になるカラーコードをランダムに生成するように変更しましょう。

実践問題


(1)変数「questionColors」を、初期値が「[](空の配列)」のuseState(状態変数)に修正してください。
(2)以下の「ランダムなカラーコード3つが定義された配列」を返却する「generateRandomColor」関数を使用して、「newGame」関数内で「questionColors」にランダムなカラーコードを設定してください。
const generateRandomColor = () => {
  const colorArray: string[] = [];

  for (let i = 0; i < 3; i++) {
    // カラーコードは「255 * 255 * 255 = 16777215」の組み合わせがあるのでその中のランダムな値を取得
    const color: string = '#' + Math.floor(Math.random() * 16777215)
        .toString(16).padStart(6, '0');
    colorArray.push(color);
  }
  return colorArray;
};

解答


(問1)解答を表示
const [questionColors, setQuestionColors] = useState<string[]>([]);

(問2)解答を表示
const newGame = () => {
  const newQuestionColors: string[] = generateRandomColor();
  setQuestionColors(newQuestionColors);
  // 新しい問題から正解のカラーコードを取得するため、newGame関数内で作成した新しい配列を参照する
  setCorrectColor(newQuestionColors[Math.floor(Math.random() * 3)]);
};

const generateRandomColor = () => {
  const colorArray: string[] = [];

  for (let i = 0; i < 3; i++) {
    const color: string = '#' + Math.floor(Math.random() * 16777215)
        .toString(16)
        .padStart(6, '0');
    colorArray.push(color);
  }
  return colorArray;
};

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

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

  useEffect(() => {
    newGame();
  }, []);

  useEffect(() => {
    if (result === '正解') {
      newGame();
    }
  }, [result]);

  const newGame = () => {
    const newQuestionColors: string[] = generateRandomColor();
    setQuestionColors(newQuestionColors);
    setCorrectColor(newQuestionColors[Math.floor(Math.random() * 3)]);
  };

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

  const generateRandomColor = () => {
    const colorArray: string[] = [];

    for (let i = 0; i < 3; i++) {
      const color: string = '#' + Math.floor(Math.random() * 16777215)
          .toString(16)
          .padStart(6, '0');
      colorArray.push(color);
    }
    return colorArray;
  };

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