🔥

React初心者のハンズオン

に公開

やりたいこと

・Reactを使って、inputタグに入力した値をリスト表示させたい。
・下記の状態をベースに、入力した値をリスト表示させたい。尚、現在表示されているリストはHTMLでベタ打ちしています。この状態から入力・表示できるようになるまで、ReactのState的な概念に慣れていない自分はどこから手をつければ良いのか迷ってしまいました。順を追って手順を記載します。

App.jsx

import "./styles.css";

export const App = () => {
  return (
    <>
      <div className="add">
        <input placeholder="enter country" />
        <button>ADD</button>
      </div>
      <div className="visited">
        <h2 className="title">Visited Countries</h2>
        <ul>
          <li>
            <div className="list-row">
              <p className="country">JAPAN</p>
            </div>
          </li>
          <li>
            <div className="list-row">
              <p className="country">KOREA</p>
            </div>
          </li>
          <li>
            <div className="list-row">
              <p className="country">CHINA</p>
            </div>
          </li>
        </ul>
      </div>
    </>
  );
};

構築の流れ

1.useState関数でStateをlist表示できるようにする。

useStateフックを利用して、dataという変数と変数の値を更新するsetData関数を使用できるようにします。変数名dataは何でも良く、今回は表示される国という意味でcountriesという変数にしました。

const [countries, setCountry] = useState(["FRANCE", "ITALY","SWITZERLAND"]);

useState()の引数には初期値を入れます。
後々入力した値をセットできるようにしますが、list表示が正常にできるかのテストをしたいので、配列に3つの国名を入れました。

list側は先ほどまで国名をベタ打ちしていましたが、
{countries}に置き換えて、先ほど定義したuseStateの変数に入っている値を表示できるように変更します。

        <ul>
          <li>
            <div className="list-row">
              <p className="country">{countries}</p>
            </div>
          </li>
        </ul>

ブラウザで表示させてみると下記のようになりました。
useStateの初期値に配列を設定しているものの、
表示させる処理で配列から要素を一つずつ表示させる処理を入れていないため、
全て結合されて表示されてしまいます。

2.mapでStateの配列から要素を取得して表示させる。

map関数を使用して、Stateの配列から要素を取り出して表示させます。

配列.map((要素, インデックス) => {
  return 新しい値;
});

map関数を使用して、配列からデータを取得するようにすると、下記の記述になります。

      <div className="visited">
        <h2 className="title">Visited Countries</h2>
        <ul>
          {countries.map((country) => {
            return (
              <li>
                <div className="list-row">
                  <p className="country">{country}</p>
                </div>
              </li>
            );
          })}
        </ul>
      </div>

正直私にはかなり難しかったのですが、こんな動きです。

map関数は引数を一つ取ります。

countries.map(コールバック関数);

引数にはコールバック関数が入り、配列の一つ一つの要素に対して関数を実行できます。

countries.map(()=>{})

つまり、map関数は配列の全要素に対して何らかの処理を実行し、その結果から新しい配列を作るメソッドです。その「何らかの処理」の部分で、各要素に適用させたい処理を記述することで、新しい配列を作る過程でリスト表示してくれているという流れになります。
私はここの仕組みを理解するのにかなり時間がかかりました。

          {countries.map((country) => {
            return (
              <li>
                <div className="list-row">
                  <p className="country">{country}</p>
                </div>
              </li>
            );

無事に配列をリスト表示させることができました。

3.Keyの設定

Reactは前回のレンダリング結果と現在の状態を比較して、差分を更新します。
リスト等の要素がある場合に、どの要素が変更されたのか識別するために、配列に対してkeyをしています。ここはReactのお約束的に覚えておきます。

          {countries.map((country) => {
            return (
              <li key={country}>
                <div className="list-row">
                  <p className="country">{country}</p>
                </div>
              </li>
            );
          })}

4.配列への追加

inputに入力したデータを配列の中に入れる。

下記の2つの関数を定義します。
1つはuseStateを使用した下記の関数です。
addedCountryという、inputに入力されたデータを格納する変数と、
addedCountryを管理するための関数です。

const [addedCountry, setAddedCountry] = useState([""]);

もう一つは、フォームにデータが入力された際に、値を取得し、先ほど定義した変数(addedCountry)にデータをセットする関数です。eventを引数として受け取っていますが、inputタグの値が変更された時に入力値をeventで受け取れるよう、HTML側の記述もこの後変更します。

  const setData = (event) => setAddedCountry(event.target.value);

inputタグ側に下記の2つを追加します。
 * value:inputの現在の値を指定。
 * onChange:フォームの入力に変更があった際に実行する処理。

        <input
          placeholder="enter country"
          value={addedCountry}
          onChange={setData}
        />

以上の記述で、
フォームの値が変わった際に、onChangeでvalueの値を買えられるようになりました。

5.buttonのclickイベントがあった際に配列にinputデータを追加する

buttonタグに、onClickイベントがあった際、onClickという関数を実行する処理を追記します。
onClick関数の中身はこの後記載します。

<button onClick={onClick}>ADD</button>

onClick関数側を定義します。
まずnewCountriesという配列を用意し、スプレッド構文で国名を入れる配列(countries)にaddedCountry(inputタグのValue)を追加した配列を作ります。
入力値を追加した配列(newCountries)をcountriesと同じ値にするために、
setCountries関数の引数に設定して、配列を更新します。

  const onClick = () => {
    const newCountries = [...countries, addedCountry];
    setCountries(newCountries);
  };

これでフォームに入力すると、リストに追加する処理ができました。

苦戦したこと

Stateの仕組みを理解するのに、非常に苦労しました。
特に最後の処理ではnewCountriesの時点で配列が完成しているので、setCountries関数をさらに実行しないといけないんだっけ?と非常に混乱しました。

  const onClick = () => {
    const newCountries = [...countries, addedCountry];
    setCountries(newCountries);
  };

実際にonClick関数内にconsole.log(newCountries);を入れてみると、配列が完成することが確認できます。

いろいろと調べたところ、setCountries(useState関数)を実行しないといけない理由は、
React は 「stateやpropsが変化したときだけ」再描画(=UIの更新)を行う仕組みであることからでした。

newCountiriesはStateではないので、useStateを実行しない限りReactをが変更を認識できないという仕組みのようです。

このあたりは、Reactを扱う上で嫌でも慣れる必要があると思いましたので、数をこなして習得していきたいです。

Discussion