最近学習としてReactを触り始めたものの、慣れない言葉や考え方が出てきて大変だったのでまとめておく。
Reactとは?から書こうとも最初思ったが、自分が忘れないようエッセンスだけ書き残しておく。
Reactフックとは
まず、Reactフック(Hooks)は、React 16.8以降で導入された機能で、関数コンポーネント内でステート(state)やライフサイクルイベントにアクセスするためのAPIのこと。フックを使うことで、クラスコンポーネントを使わずに状態管理や副作用を扱うことができる。
難しい。これだけ言われてもわからないので、一つ一つ解説を残す。
コンポーネントとは
コンポーネントは、Reactアプリケーションの一部分で、画面上の要素(ボタン、テキストボックス、画像など)を作成するための再利用可能なコードのかたまりである。コンポーネントを使って、アプリケーションの画面を小さな部品に分割し、それらを組み立てることで、全体の画面を作成することができる。
例えば、ショッピングサイトのUIを考えてみる。ヘッダー、サイドバー、商品リスト、フッターなど、いくつかの部分に分けることができる。これらの部分はそれぞれ独立したコンポーネントとして作成され、それらを組み合わせることで全体の画面が構成される。
コンポーネントは、関数コンポーネントとクラスコンポーネントの2種類があるが、どちらも同じ目的で使用される。関数コンポーネントは、よりシンプルで短いコードで書くことができるが、クラスコンポーネントはより複雑な機能を実装することができる。最近では、Reactフックを使って、関数コンポーネントでもクラスコンポーネントと同様の機能を実現できるため、関数コンポーネントがよく使われている。
簡単に言えば、コンポーネントはReactアプリケーションの小さな部品で、再利用可能なコードを作成し、それらを組み合わせて全体の画面を構築するために使用される。
ステート(state)とは
stateとは、コンポーネント内で管理されるデータのこと。stateが変更されると、コンポーネントが再レンダリング(描画)され、新しいstateの値に基づいた表示が行われる。これによって、動的なUIを実現することができる。関数コンポーネントでは、useStateフックを使ってstateを管理する。
ライフサイクルイベントとは
ライフサイクルとは、コンポーネントがReactアプリケーションで経る一連のフェーズやステップのことを指す。Reactでは、コンポーネントがアプリに表示されたり、アップデートされたり、破棄されたりする際に、これらのフェーズを通過する。ライフサイクルは、主に以下の3つのフェーズで構成されている。
マウント(Mounting): これは、コンポーネントがDOMに初めて追加されるフェーズである。コンポーネントは、このフェーズで初期化や初期レンダリングが行われる。
更新(Updating): このフェーズでは、コンポーネントが再レンダリングされることで、状態やプロパティの変更が反映される。状態やプロパティが変更されると、Reactはコンポーネントの再レンダリングを行い、DOMが更新される。
アンマウント(Unmounting): このフェーズでは、コンポーネントがDOMから削除される。このフェーズで、コンポーネントに関連するリソースのクリーンアップや解放が行われる。
関数コンポーネントでは、useEffectフックを使用して、これらのライフサイクルイベントに対応するロジックを実装できる。例えば、useEffectで副作用を定義し、クリーンアップ関数を返すことで、マウント、更新、アンマウントの各フェーズで実行される処理を制御できる。
自分がよく使ったフック3選
フック | 説明 | 用途 |
---|---|---|
useState | 状態を持たせるためのフック。第一引数に現在の状態と状態を更新する関数が返される | コンポーネントのローカル状態を管理する |
useEffect | 副作用を実行するためのフック。コンポーネントのマウント、アンマウント、または状態変更時に実行される。 | データの取得、購読、または手動でのDOMの変更などの副作用を実行する。 |
useContext | コンテキストを使用するためのフック。コンテキストの現在値を返す。 | 親コンポーネントから子コンポーネントへデータを渡す。 |
useState
関数コンポーネントとuseStateを使った簡単なカウンターアプリの例を見てみる。
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
export default Counter;
この例では、Counterという関数コンポーネントを作成している。useStateフックを使って、countという状態変数と、setCountという状態を更新する関数を定義している。useState(0)で、countの初期値を0に設定している。
return内のJSXでは、現在のカウント値(count)を表示し、ボタンをクリックしたらカウントが1増えるようになっている。onClickイベントで、setCount(count + 1)としてcountを更新している。
このコードでは、countが状態変数(state)で、setCountが状態更新関数である。ボタンをクリックすることでcountが更新され、コンポーネントが再レンダリングされ、最新のカウント値が表示される。
このように、関数コンポーネントとuseStateを使って、状態(state)を管理し、状態が変わることでUIが更新される仕組みが実現できる。
なんでimport React, { useState } from 'react';
みたいに{}で囲むのとそうじゃないのがあるの?
JavaScriptのインポート構文には、名前付きインポート(Named imports)とデフォルトインポート(Default imports)という2つの方法がある。それぞれの使い方と違いは以下の通り。
名前付きインポート (Named imports):
{}を使うのは、名前付きインポートである。これは、モジュールから特定の名前でエクスポートされた値をインポートする方法である。{}内にインポートしたい名前を指定する。
例
import { useState, useEffect } from 'react';
この例では、reactモジュールからuseStateとuseEffectという名前の値をインポートしている。
デフォルトインポート (Default imports):
デフォルトインポートは、モジュールからデフォルトでエクスポートされた値をインポートする方法のこと。{}を使わずにインポートする。デフォルトでエクスポートされた値は1つだけ存在できる。
例:
import React from 'react';
この例では、reactモジュールからデフォルトでエクスポートされた値(Reactオブジェクト)をインポートしている。
したがって、import React, { useState } from 'react';
という構文では、reactモジュールからデフォルトインポートされたReactオブジェクトと、名前付きインポートされたuseState関数を同時にインポートしていることになる。
モジュールって何よ
モジュールとは、JavaScriptのコードを構成する単位のこと。コードを複数のファイルに分割し、それぞれをモジュールとして管理することで、コードの再利用や可読性、保守性が向上する。モジュールは、独自のスコープを持っており、他のモジュールと変数や関数の名前が衝突しないようになっている。
モジュールは、他のモジュールから利用される関数や変数、クラスなどをエクスポート(export)することができる。また、必要な機能を他のモジュールからインポート(import)して利用することができる。
モジュールの例:
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
このmath.jsモジュールでは、add関数とsubtract関数をエクスポートしている。
他のモジュールでmath.jsの関数を利用するには、次のようにインポートする。
// app.js
import { add, subtract } from './math.js';
console.log(add(1, 2)); // 3
console.log(subtract(4, 1)); // 3
このapp.jsモジュールでは、math.jsモジュールからadd関数とsubtract関数をインポートし、それらを使用している。
コンポーネントとは違うの?
コンポーネントとモジュールは、異なる概念であるものの、関連性はある。
コンポーネントは、主にReactにおいて、UI(ユーザーインターフェース)の構成要素を再利用可能な部品として表現したもの。コンポーネントは、独立した機能を持ち、他のコンポーネントと組み合わせることでより複雑なUIを構築できる。Reactでは、関数コンポーネントやクラスコンポーネントといった形で実装される。
モジュールは、JavaScriptコードの管理・再利用性を高めるための仕組みで、関数や変数、クラスなどを他のファイルからインポートできるようにする機能のこと。モジュールはReactに限らず、JavaScript全般で使用される概念である。
コンポーネントとモジュールの関連性は、Reactのコンポーネントも、JavaScriptのモジュールとしてエクスポート・インポートされることである。つまり、Reactコンポーネントは、モジュールの一種として扱われる。
例えば、以下のようなReactコンポーネントがあるとする。
// Button.jsx
import React from 'react';
const Button = (props) => {
return (
<button onClick={props.onClick}>{props.children}</button>
);
};
export default Button;
このコンポーネントは、モジュールとしてエクスポートされており、他のファイルからインポートして利用することができる。
// App.jsx
import React from 'react';
import Button from './Button';
const App = () => {
const handleClick = () => {
alert('Button clicked!');
};
return (
<div>
<Button onClick={handleClick}>Click me</Button>
</div>
);
};
export default App;
このように、コンポーネントはReactにおけるUIの部品であり、モジュールはJavaScriptコードの構成単位であることを理解しておくと良い。
useEffect
useEffectは、Reactのフックの1つで、副作用(side effects)を実行するために使用される。
副作用(side effects)とは、プログラミングにおいて、関数やコンポーネントの主要な目的以外で起こる影響のことを指す。主に、外部の状態や変数に影響を与える操作が副作用とされる。
Reactのコンポーネントにおいて、副作用は以下のようなものがある。
1 APIからデータを取得する
2 データをストアに保存する
3 タイマーやイベントリスナーを設定する
4 ブラウザのlocalStorageへのアクセス
5 手動でDOMを変更する
useEffectは、関数コンポーネント内で副作用を実行するタイミングを制御するために使用される。useEffectに渡される関数は、コンポーネントのマウント(初回描画)、アンマウント(コンポーネントが破棄されるタイミング)、および依存関係が変更された際に実行される。
useEffectを使った例。
import React, { useState, useEffect } from 'react';
function SimpleCounter() {
// カウントの状態を保持するstateを定義
const [count, setCount] = useState(0);
// カウントが変更されたときに実行される副作用を定義
useEffect(() => {
console.log('カウントが更新されました:', count); // 副作用: カウントが更新されたときにコンソールにログを出力
}, [count]); // 依存配列にcountを指定することで、countが変更されたときだけ実行される
return (
<div>
<p>現在のカウント: {count}</p> {/* countの値を表示 */}
<button onClick={() => setCount(count + 1)}>カウントアップ</button> {/* countを増やすボタン */}
</div>
);
}
export default SimpleCounter;
useContext
コンテキストは、Reactでデータをコンポーネントのツリーを通して渡す方法であり、親コンポーネントから子コンポーネントへのデータの受け渡しを容易にするための仕組みのことである。これにより、プロップス(親コンポーネントから子コンポーネントに渡されるデータ)を使わずにデータを共有できる。
useContextは、Reactのフックの1つで、このコンテキストを使ってデータを共有するために使われる。
例として、ThemeContextを作成し、それを使用してAppコンポーネントからChildコンポーネントにデータを渡すことを考える。この例では、AppコンポーネントからChildコンポーネントにテーマカラーを渡す。
まず、App.tsxファイルを作成し、以下のようなコードを書く。
// App.tsx
import React, { useState, useContext } from "react";
// 1. ThemeContextを作成
const ThemeContext = React.createContext("");
function App() {
// 2. テーマカラーをstateで管理
const [themeColor, setThemeColor] = useState("red");
return (
// 3. ThemeContext.Providerを使って、データを子コンポーネントに渡す
<ThemeContext.Provider value={themeColor}>
<Child />
<button onClick={() => setThemeColor("blue")}>Change theme color to blue</button>
</ThemeContext.Provider>
);
}
function Child() {
// 4. useContextフックを使って、データを受け取る
const themeColor = useContext(ThemeContext);
return (
// 5. 受け取ったデータを表示
<p>Theme color is: {themeColor}</p>
);
}
export default App;
説明を加える。
1 React.createContextを使って新しいコンテキストオブジェクトを作成する。この例では、ThemeContextという名前にした。
2 useStateを使って、テーマカラーを管理するthemeColorと、それを更新するsetThemeColorを定義する。初期値はred。
3 ThemeContext.Providerコンポーネントを使って、themeColorを子コンポーネントに渡す。value属性でデータを指定する。
4 Childコンポーネントでは、useContextフックを使ってThemeContextからデータを受け取る。この例では、themeColorを受け取る。
5 受け取ったデータ(themeColor)を表示する。
このコードを実行すると、最初にChildコンポーネントが「Theme color is: red」と表示される。ボタンをクリックすると、setThemeColorが呼ばれてテーマカラーがblueに変わり、「Theme color is: blue」と表示される。
ちなみに、ThemeContext.Providerのように、Providerをつけるのは、ReactのコンテキストAPIの慣習らしい。Providerは「データを提供する役割をもつ」ということだそう。
まだまだ大事なことはあるが、このフックを少しでも使えるようになると上達へ一歩近づくと思う。日々頑張ろう。