【第9回】Reactを使ってみよう

Reactをつかってより効率よく動的なWebページを作成してみましょう。

Homeブログ一覧【第9回】Reactを使ってみよう

今回からはReactを使って、より効率よく簡単に動的なWebページを作成していきます。

Reactとは

ReactはFacebookが開発したJavaScriptのライブラリです。 Reactを使うことで、HTMLの要素をJavaScriptで動的に操作することができます。

このとき、「HTMLの要素をJavaScriptで動的に操作する」というのを「DOM操作」と呼びます。 DOMとは、Document Object Modelの略で、HTMLの要素をJavaScriptで操作するための仕組みのことです。 そして、ReactではこのDOM操作を「仮想DOM」という仕組みでより効率的に行うことができます。

素のJavaScriptとReactの比較

それでは、素のJavaScriptとReactを使ったDOM操作の違いを見ていきましょう。

要素の表示と作成

まずは、要素を表示する方法を見ていきます。

素のJavaScriptでは、document.createElementを使って要素を作成し、appendChildを使って要素を表示します。

// <div>を作成
const element = document.createElement("div");
// <div>Hello, world!</div>を作成
element.textContent = "Hello, world!";
// <div>Hello, world!</div>を<body>の子要素として追加し、表示
document.body.appendChild(element);

一方、Reactでは、React.createElementを使って要素を作成し、ReactDOM.renderを使って要素を表示します。 ただし、「JSX」という構文を使うことで、React.createElementを使わずに要素を作成することができます。

import React from "react";
import ReactDOM from "react-dom";

// <div>Hello, world!</div>を作成
const App = () => {
  return <div>Hello, world!</div>;
};

ReactDOM.render(<App />, document.body);

要素の更新

次に、要素を更新する方法を見ていきます。 たとえば、「ボタンが押されたら、<p>の中身を変更する」という処理を考えてみましょう。

素のJavaScriptでは、addEventListenerを使ってイベントを登録し、textContentを使って要素の中身を変更します。

// <div>を作成
const element = document.createElement("div");

// <p>Hello, world!</p>を作成
const paragraph = document.createElement("p");
paragraph.textContent = "Hello, world!";

// <button>Click me!</button>を作成
const button = document.createElement("button");
button.textContent = "Click me!";
// <button>がクリックされたら、<p>の中身を変更
function handleClick() {
  paragraph.textContent = "Hello, Maximum!";
};
button.addEventListener("click", handleClick);

// <p>と<button>を<div>の子要素として追加し、表示
element.appendChild(paragraph);
element.appendChild(button);

一方、Reactでは、React.useStateを使って状態を管理し、onClickを使ってイベントを登録します。

import React from "react";
import ReactDOM from "react-dom";

// <div>Hello, world!</div>を作成
function App() {
  const [text, setText] = React.useState("Hello, world!");

  function handleClick() {
    setText("Hello, Maximum!");
  };

  return (
    <div>
      <p>{text}</p>
      <button onClick={handleClick}>
        Click me!
      </button>
    </div>
  );
};

ReactDOM.render(<App />, document.body);

コードの実質的な長さで比べればおそらく素のJavaScriptの方が短いですが、Reactでは状態を管理することで、要素の更新をより簡単に行うことができます。 また、JSXを使うことでHTMLのように直接要素を記述することができるため、可読性も高くなります。(厳密には、JSXはHTMLではない。)

Reactのユースケースや活用などについて

Reactはカテゴリ的には「フロントエンドフレームワーク」と呼ばれるものに分類されます。 フロントエンドフレームワークとは、フロントエンドの開発を効率化するためのライブラリのことです。 Reactの他にも、AngularやVue.jsなどのフロントエンドフレームワークがあります。もしかしたら聞いたことがあるかもしれません。

Reactは今現在、フロントエンドフレームワークの中でも最も人気があり、多くの企業で採用されています。 派生として

  • React Native (モバイルアプリ開発)
  • Next.js (サーバーサイドレンダリングできるReact)

などがあります。どの技術に派生するにしても、このReactを知っておくことは非常に重要です。

Reactの開発について

Reactの開発には、Node.jsとnpmが必要です。 Reactをビルドした後はブラウザでも動くのですが、開発中やビルドをする行為にはNode.jsが必要です。 Node.jsのインストールは以前の記事を参考にしてください。

Reactの開発環境の構築

今回はViteというツールを使ってReactの開発環境を構築します。 (こちらも話すと長くなるのであまり紹介しませんが、「バンドラ」というカテゴリのツールです。)

Viteを使うと、Reactの開発環境を簡単に構築することができます。 Viteを使ってReactの開発環境を構築するには、以下のコマンドを実行します。

npm create vite

すると、いくつか質問が表示されるので、以下のように回答します。

Project name: › first-react-app
Select a framework: › react
Select a variant: › JavaScript

すると、first-react-appというディレクトリが作成され、Reactの開発環境が構築されます。

first-react-appディレクトリに移動し、先にGitの初期化をしておきましょう。

cd first-react-app
git init

first-react-appディレクトリ内で、以下のコマンドを実行すると、Reactの開発用サーバーが起動します。

npm run dev

起動したら、ブラウザでhttp://localhost:5173にアクセスすると、Reactの開発用サーバーが起動していることが確認できます。

サンプルコードが生成されていますが、今回は使わないので、余計なファイルを削除しておきましょう。

rm src/App.css
rm src/index.css
rm -rf src/assets
rm -rf public

さらに不要なコードも削除します。

src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
- import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
src/App.jsx
function App() {
  return (
    <div>Hello World!</div>
  )
}

export default App

App.jsxの方は書き換える部分が多いのでそのまま上書きしてください。

ここでコミットしましょう

git add .
git commit -m "Reactのプロジェクトを初期化"

Reactの基本(入門)

Reactの基本的な使い方について見ていきます。

コンポーネント

Reactでは、要素を「コンポーネント」という単位で分割して管理します。 いままで全て1ファイルのHTMLに書いていたものを、コンポーネントという単位で分割して管理することで、可読性を高めることができます。

たとえばサイトにHeaderとMainとFooterという3つのエリアがあるとしたら、

<body>
    <header>
    <h1>Web研ブログ</h1>
    </header>
    <main>
    <article>
        <time>2023-06-19</time>
        <h2>【第9回】Reactを使ってみよう</h2>
        <p>Reactをつかってより効率よく動的なWebページを作成してみましょう。</p>
    </article>
    <article>
        <time>2023-06-12</time>
        <h2>【第8回】JavaScriptを使ってみよう</h2>
        <p>JavaScriptをつかってより動的なWebページを作成してみましょう。</p>
    </article>
    <article>
        <time>2023-06-05</time>
        <h2>【第7回】HTMLとCSSを使ってみよう</h2>
        <p>HTMLとCSSをつかってより見た目のきれいなWebページを作成してみましょう。</p>
    </article>
    </main>
    <footer>
    <p>© 2023 Web研究会</p>
    </footer>
</body>

これをReactのコンポーネントに分けると、

function Header() {
  return (
    <header>
      <h1>Web研ブログ</h1>
    </header>
  );
};

function Main() {
  return (
    <main>
        <article>
            <time>2023-06-19</time>
            <h2>【第9回】Reactを使ってみよう</h2>
            <p>Reactをつかってより効率よく動的なWebページを作成してみましょう。</p>
        </article>
        <article>
            <time>2023-06-12</time>
            <h2>【第8回】JavaScriptを使ってみよう</h2>
            <p>JavaScriptをつかってより動的なWebページを作成してみましょう。</p>
        </article>
        <article>
            <time>2023-06-05</time>
            <h2>【第7回】HTMLとCSSを使ってみよう</h2>
            <p>HTMLとCSSをつかってより見た目のきれいなWebページを作成してみましょう。</p>
        </article>
    </main>
  );
};

function Footer() {
  return (
    <footer>
      <p>© 2023 Web研究会</p>
    </footer>
  );
};

function App() {
  return (
    <>
      <Header />
      <Main />
      <Footer />
    </>
  );
};

ReactDOM.render(<App />, document.body);

このブログもいろいろなコンポーネントの組み合わせで構成されていて、例えばブログ一覧ページをコンポーネントに分けるとこんな感じです。

ブログ一覧ページのコンポーネント

State

Reactでは、要素の状態を「State」という単位で管理します。 ここでいう「状態」の定義は「アプリケーション内で変化する値」です。 アプリケーションはサイトの中に構築されているので、語弊を恐れず言えば状態は「リロードしなくても変化する値」です。

それではStateを使った適当なアプリを作ってみましょう。

src/App.jsx
import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  };

  return (
    <div>
      <p>{count}</p>
      <button onClick={handleClick}>
        Click me!
      </button>
    </div>
  );
};

export default App;

ReactからuseStateをインポートして、useStateを使って状態を管理します。 これだけでボタンを押すとカウントアップするアプリが作成できました。

useStateは、useState(初期値)という形で使います。今回は初期値を0にしています。 useStateは、[状態, 状態を変更する関数]という形で値を返します。今回は、countという状態と、setCountという状態を変更する関数の2つを返しています。

この[○○○, set○○○]という形は、Reactの慣習としてよく使われますので覚えておきましょう。 そして最後に状態の値が入った変数であるcount<p>の中に表示しています。 {変数や定数など}という形で、変数や定数などをJSX内に埋め込むことができます。

set...を呼ぶと初めて表示されているDOMが更新されます。 状態を更新する時count = count + 1というように、直接状態を更新しようとすると、React側が変更を検知できずに表示が更新されませんので、必ずset...を使って状態を更新するようにしましょう。

それでは、このアプリをコミットしましょう。

git add .
git commit -m "Stateを使ったカウントアップアプリを作成"

次に作ったカウンターをコンポーネントとして分割してみましょう。

src/components/Counter.jsx
import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  };

  return (
    <div>
      <p>{count}</p>
      <button onClick={handleClick}>
        Click me!
      </button>
    </div>
  );
};

export default Counter;
src/App.jsx
import Counter from "./components/Counter";

function App() {
  return (
    <Counter />
  );
};

export default App;

Counterというコンポーネントを作成し、Appコンポーネントから呼び出しています。 これで、カウントアップアプリをコンポーネントとして分割することができました。

このように、コンポーネントとStateを組み合わせることで、より複雑なアプリを作成することができます。

とりあえずこれでコミットしましょう。

git add .
git commit -m "カウントアップアプリをコンポーネントとして分割"

今までの知識を使ってTodoアプリを作ってみよう

ここまでの知識を使って、Todoアプリを作ってみましょう。

必要な状態を考えよう

Todoアプリを作るにあたって、どのような状態が必要になるか考えてみましょう。

  • Todoを入力するためのフォームの値
  • Todoの配列

この2つの状態が必要になります。

フォームの値を管理する

まずは、フォームの値を管理するための状態を作成します。

src/App.jsx
import { useState } from "react";

function App() {
  const [todoInput, setTodoInput] = useState("");

  function handleChange(event) {
    setTodoInput(event.target.value);
  };

  return (
    <div>
      <input type="text" value={todoInput} onChange={handleChange} />
      <p>{todoInput}</p>
    </div>
  );
};

export default App;

useStateを使って、todoInputという状態と、setTodoInputという状態を変更する関数を作成しています。 <input>valuetodoInputを指定することで、フォームの値をtodoInputに紐付けています。

さらに<input>の値が変更された時に呼ばれるonChangehandleChangeを指定することで、フォームの値が変更された時に毎回handleChangeが呼ばれるようにしています。

入力された値はevent.target.valueで取得できるので、setTodoInputを使ってtodoInputを更新しています。

これで、フォームの値を管理するための状態を作成することができました。

コミットしましょう。

git add .
git commit -m "フォームの値を管理するための状態を作成"

Todoの配列を管理する

次に、Todoの配列を管理するための状態を作成します。 開発しやすいようにtodos配列の初期値には適当なTodoを入れておきましょう。

src/App.jsx
import { useState } from "react";

function App() {
  const [todoInput, setTodoInput] = useState("");
  const [todos, setTodos] = useState(["掃除", "洗濯", "買い物"]);

  function handleChange(event) {
    setTodoInput(event.target.value);
  };

  return (
    <div>
      <input type="text" value={todoInput} onChange={handleChange} />
      {todos.map((todo, i) => <p key={i}>{todo}</p>)}
    </div>
  );
};

export default App;

これで、Todoの配列を管理するための状態を作成することができました。 todos.map((todo, i) => <p key={i}>{todo}</p>)という部分は、todos配列の中身を<p>で囲んで表示しています。 key={i}というのは、Reactの慣習としてよく使われるもので、keyという属性には一意な値を指定する必要があります。 これを正しく指定しないと、Reactが要素の更新を検知できなくなるので、必ず指定するようにしましょう。 今回はiを指定していますが、imap引数の関数の第二引数で渡されるインデックスです。 注意して欲しいのはmapkey={i}の部分です。keyには一意な値を指定する必要があります。 並び替えや削除を行うとkeyが変わってしまうので、Reactが特定できなくなります。 idやtimestampなど、絶対に被らない値を入れるように工夫してみてください。

以前SSRを文字列で行った時にも同じようなことをしていましたね。それと同じです。

これで表示部分は完成しました。

コミットしましょう。

git add .
git commit -m "Todoの配列を管理するための状態を作成"

Todoを追加する

最後に、Todoを追加する処理を実装します。 ボタンを押したらフォームの値をtodos配列に追加する処理を実装しましょう。

src/App.jsx
import { useState } from "react";

function App() {
  const [todoInput, setTodoInput] = useState("");
  const [todos, setTodos] = useState(["掃除", "洗濯", "買い物"]);

  function handleChange(event) {
    setTodoInput(event.target.value);
  };

  function handleClick() {
    const newTodos = [...todos, todoInput];
    setTodos(newTodos);
    setTodoInput("");
  };

  return (
    <div>
      <input type="text" value={todoInput} onChange={handleChange} />
      <button onClick={handleClick}>追加</button>
      {todos.map((todo,i) => <p key={i}>{todo}</p>)}
    </div>
  );
};

export default App;

新たにhandleClickという関数を作成し、ボタンが押されたらhandleClickが呼ばれるようにしています。 handleClickでは、newTodosという変数に、既存のtodos配列とフォームの値を結合した配列を新たに作成しています。 そして、setTodosを使ってtodos配列を更新しています。 最後に、フォームの値を空にするためにsetTodoInputを使ってtodoInputを更新しています。

これで動くと思うので、試してみましょう。

コミットしましょう。

git add .
git commit -m "Todoを追加する処理を実装"

課題(任意)

次回解説します

  • Todoを削除する処理を実装してみよう
  • Todoを完了済みにする処理を実装してみよう
  • 並べ替え機能を実装してみよう