はじめに
React初心者が学習で理解した基本的なことを備忘録として記載していきます。
環境
Mac OS: Big Sur 11.6
npm: 8.3.1
Reactについて
ReactはSPA(シングルページアプリケーション)です。
シングルページアプリケーションについては以下のページに書いてあります。
SPA(シングルページアプリケーション)って何者
ざっくりと言うとページをロードすることなくページが更新されていく仕組みです。
例えばtwitterやFacebookなど。
Reactプロジェクト作成
Create React Appを使用してアプリを作成します。
$ npx create-react-app myapp
Reactの構成
基本的にこの3つのファイルが基盤でApp.js内に子コンポートネントを配置していく。
public
∟ index.html ... SPAの基盤となるテンプレートです。基本的には触らない。
src
∟ index.js ... index.html内の<div id="root"></div>にApp.jsをレンダリングしている。
∟ App.js ... コンポーネントの基盤。この配下に子コンポーネントを作っていく。
コンポーネントとは
画面のパーツのこと。
例えば画面のボタンのみを切り出して別ファイルとして管理する。
その中にボタンのクリック処理などを設定する。
パーツとして使えるため再利用等ができる。コンポーネントのファイルを呼び出す場合はimportし、コンポーネント側はexportする。
コンポーネントファイル側でボタンを作成する。ボタンを押すとアラートが実行される。
①コンポーネントファイル作成する /src/components/SpecialButton.js
(src配下に自作でcomponentsフォルダを作成するとする。)
export const SpecialButton = () => {
function buttonImplementing() {
alert("ボタンが押された!");
}
return (
<button onClick={buttonImplementing} />
);
};
②コンポーネントファイルを設置する。/src/App.js
import { SpecialButton } from "./components/SpecialButton"; // インポートする
export const App = () => {
return (
<div>
<SpecialButton /> {/** コンポーネントを設置 */}
</div>
);
};
Props
コンポーネント側に値を渡すこと。
①呼び出し元よりPropsを渡す。/src/App.js
import { SpecialButton } from './components/SpecialButton';
export const App = () => {
return (
<div>
{/** コンポーネントにmessageとbuttonNameをpropsで値を渡す */}
<SpecialButton message="ボタンを押しました" buttonName="ボタンの名前" />
</div>
);
}
export default App;
②コンポーネント側で値をpropsで受け取る。 /src/components/SpecialButton.js
export const SpecialButton = (props) => {
function buttonImplementing() {
alert(props.message); // messageを受け取ってアラートを実行する。
}
return (
{/** buttonNameを受け取ってボタン名を表示する。 */}
<button onClick={buttonImplementing} >{props.buttonName}</button>
);
};
State
コンポーネント内にあるデータの状態を管理することができる。
React Hooks(関数)内のuseStateを使用してStateを扱う。
/src/components/SpecialButton.js
import { useState } from "react"; // React HooksよりuseStateをインポートする
export const SpecialButton = (props) => {
// count: 一つ目がコンポネート内で保持されるデータ、
// setCount: 2つ目がデータをセットする関数
const [count, setCount] = useState(0); // 初期値に0をセットする。
function buttonImplementing() {
setCount((count) => count + 1); // 現在のカウンタをインクリメントし関数でセットする。
console.log(count); // ボタンを押すとcountの値が0,1,2,3と変わっていく。
}
return (
<button onClick={buttonImplementing} >{props.buttonName}</button>
);
};
再レンダリング / useEffect
再レンダリングの概念
コンポーネント内部でStateが変更されたことにより、コンポネート内の表示が変わるのはDOMが再レンダリングされているため。
下の場合はボタンを押すとStateで保持しているcountがカウントアップされ、「カウント数:{count}」の表示が変わる。これもコンポネートの再レンダリングを行い表示を変更している。
import { useEffect, useState } from "react";
export const SpecialButton = (props) => {
const [count, setCount] = useState(0);
function buttonImplementing() {
setCount((count) => count + 1); // カウントアップする
}
return (
<div>
<button onClick={buttonImplementing} >{props.buttonName}</button>
{/** ボタンが押される度にcountがカウントアップして表示が変わっていく */}
<p>カウント数:{count}</p>
</div>
);
};
再レンダリングが起きる条件
- Stateが更新されたコンポーネント
- Propsが変更されたコンポーネント
- 再レンダリングされたコンポーネント配下のコンポーネント全て
useEffect
React Hooksには再レンダリングした際に、あるデータが変更された場合に再レンダリングした後に実行できるuseEffectがある。
下記はコンポネート内のstateのcountに変更があった場合に再レンダリングされた後にアラートが実行される。
import { useEffect, useState } from "react"; // useEffectをインポート
export const SpecialButton = (props) => {
const [count, setCount] = useState(0);
useEffect( () => {
alert("再レンダリングされました。"); // 第1引数に実行する処理を記述する。
}, [count]); // 第一引数を実行するためのトリガーとなる依存データを記述する。
// countの値が変わった際(つまりボタンを押した際)に第一引数の記述したアラートが実行される。
function buttonImplementing() {
setCount((count) => count + 1);
console.log(count); // ボタンを押すと0,1,2,3と値が変わっていく。
}
return (
<button onClick={buttonImplementing} >{props.buttonName}</button>
);
};
コンポネートは何度も再レンダリングされるので、特定の場合のみ実行したいことがある場合に有効な使い方ができる。
グローバルのState管理
Stateの値などを小コンポートネントにPropsで渡すことができるが、コンポートネントの構成が深くなると管理が複雑化してしまう。
下の例だとA.jsからH.jsやI.jsまでstate値をPropsで渡すとかなり大変になる。
バケツリレーのようにpropsで値を渡していくことになる。
App.js
∟ A.js ... App.js内に配置
∟ B.js ... A.js内に配置
∟ C.js ... B.js内に配置
∟ F.js ... C.js内に配置
∟ H.js ... F.js内に配置
∟ G.js ... C.js内に配置
∟ I.js ... G.js内に配置
∟ D.js ... App.js内に配置
∟ E.js ... D.js内に配置
React HooksのuseContextを使用するとグローバルに管理ができて、バケツリレーのようにpropsで値を渡す必要がなくなります。
SampleFlagという値をグローバルState管理する例を記載します。
フォルダ構成
[src]
∟ [components]
∟ componentA.js ... App.js配下にコンポーネント設置
∟ componentB.js ... componentA.js配下にコンポーネント設置
∟ componentC.js ... componentB.js配下にコンポーネント設置
∟ [providers]
∟ SampleFlagProvider.js ... SampleFlagというStateをグローバル管理するファイル
∟ App.js
∟ index.js
①SampleFlagProvider.jsを作成
import { createContext, useState } from "react";
export const SampleFlagContext = createContext({});
export const SampleFlagProvider = props => {
// stateを定義。デフォルトにFALSEをセットする。
const [ SampleFlag, setSampleFlag ] = useState(false);
return (
<SampleFlagContext.Provider value={{ SampleFlag, setSampleFlag }}>
{props.children}
</SampleFlagContext.Provider>
);
}
②App.jsにComponentA.jsを配置
import { ComponentA } from './components/ComponentA';
export const App = () => {
return (
<div>
<ComponentA />
</div>
);
}
③ComponentA.jsにComponentB.jsを配置
またグローバルStateを呼び出しする。
import { useContext, useEffect, useState } from "react";
import { ComponentB } from "./ComponentB";
import { SampleFlagContext } from "./providers/SampleFlagProvider";
export const ComponentA = (props) => {
// useContextでグローバルStateを呼び出し
const { SampleFlag, setSampleFlag } = useContext(SampleFlagContext);
function alertComponentA() {
setSampleFlag(true); // SampleFlagにtrueを設定
if(SampleFlag) {
alert("SampleFlagはTRUEです!"); // このアラートが実行される
}else {
alert("SampleFlagはFALSEです!");
}
}
return (
<div>
<p>コンポーネントA</p>
<button onClick={alertComponentA}>コンポーネントA</button>
<ComponentB />
</div>
);
};
④ComponentB.jsにComponentC.jsを配置
import { useEffect, useState } from "react";
import { ComponentC } from "./ComponentC";
export const ComponentB = (props) => {
return (
<div>
<p>コンポーネントB</p>
<ComponentC />
</div>
);
};
⑤ComponentC.jsでグローバルStateを呼び出す
import { useContext, useEffect, useState } from "react";
import { SampleFlagContext } from "./providers/SampleFlagProvider";
export const ComponentC = (props) => {
const { SampleFlag } = useContext(SampleFlagContext); // useContextでグローバルStateを呼び出し
function alertComponentC() {
// 上の階層のコンポーネント(ComponentA)でTRUEを設定している
if(SampleFlag) {
alert("SampleFlagはTRUEです!"); // このアラートが実行される
}else {
alert("SampleFlagはFALSEです!");
}
}
return (
<div>
<p>コンポーネントC</p>
<button onClick={alertComponentC}>ボタン</button>
</div>
);
};
デフォルトはFALSEだが親コンポーネントComponentA.jsでTRUEを設定しているため、
ComponentC.jsのボタンを押すと「"SampleFlagはTRUEです!"」のアラートが実行される。