今回からは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
さらに不要なコードも削除します。
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>,
)
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を使った適当なアプリを作ってみましょう。
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を使ったカウントアップアプリを作成"
次に作ったカウンターをコンポーネントとして分割してみましょう。
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;
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つの状態が必要になります。
フォームの値を管理する
まずは、フォームの値を管理するための状態を作成します。
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>
のvalue
にtodoInput
を指定することで、フォームの値をtodoInput
に紐付けています。
さらに<input>
の値が変更された時に呼ばれるonChange
にhandleChange
を指定することで、フォームの値が変更された時に毎回handleChange
が呼ばれるようにしています。
入力された値はevent.target.value
で取得できるので、setTodoInput
を使ってtodoInput
を更新しています。
これで、フォームの値を管理するための状態を作成することができました。
コミットしましょう。
git add .
git commit -m "フォームの値を管理するための状態を作成"
Todoの配列を管理する
次に、Todoの配列を管理するための状態を作成します。
開発しやすいようにtodos
配列の初期値には適当なTodoを入れておきましょう。
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
を指定していますが、i
はmap
の引数の関数の第二引数で渡されるインデックスです。
注意して欲しいのはmap
のkey={i}
の部分です。key
には一意な値を指定する必要があります。
並び替えや削除を行うとkey
が変わってしまうので、Reactが特定できなくなります。
idやtimestampなど、絶対に被らない値を入れるように工夫してみてください。
以前SSRを文字列で行った時にも同じようなことをしていましたね。それと同じです。
これで表示部分は完成しました。
コミットしましょう。
git add .
git commit -m "Todoの配列を管理するための状態を作成"
Todoを追加する
最後に、Todoを追加する処理を実装します。
ボタンを押したらフォームの値をtodos
配列に追加する処理を実装しましょう。
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を完了済みにする処理を実装してみよう
- 並べ替え機能を実装してみよう