Reactを学ぶ上で切り離すことのできないHooksという存在について、
今回は状態管理を主に説明するため「コンポーネント間の値受け渡し」の話に絞ってまとめたいと思います。
第一歩ということでなるべく分かりやすく話を進めていきます。そのため文章量が多いのでご了承ください。
TL;DR
- 状態管理とはUIではなく、UIに関連したデータを管理すること
- コンポーネント間で値を受け回す時はuseContextが便利
- Hooksはそんなに難しくないよ!
そもそも「コンポーネント」とは
Reactフレームワークというものは、UIの一部分となるViewを切り離して「コンポーネント」という形で管理をおこないます。
ざっくり言ってしまえばHTMLでズラーっと書かれたコードから、divタグで区切られたUIの1パーツ(と関連する処理)を切り離して別ファイルで保存しておくようなものです。
別ファイルで保存しておくことで、同じコードを何度も書かなくてもそのファイルを呼び出せば再利用ができるわけです。便利!
import React from 'react';
export const TestComponent = () => {
return <h1>Hi!</h1>;
}
import React from 'react';
import { TestComponent } from 'component.tsx';
export const IndexComponent = () => {
return (
<div>
<TestComponent /> /** コンポーネントの呼び出し */
<h1>タイトル</h1>
<TestComponent /> /** コンポーネントの呼び出し */
</div>
);
}
実行結果
Hi!
タイトル
Hi!
状態管理とは?
コンポーネントとはあくまでUIの一部分を切り出したものに過ぎないため、そのままでは変数の移り変わりによって動的なデザインの変更や入力内容の表示のような「画面の更新」や「値の状態の保持」ができません。
状態管理とは、「データの状態を管理することでUI全ての管理をせずに動的な更新を行えるようにする」機能です。
これを応用して、ある変数の状態を監視することで何か変化があった際にレンダー(画面描画)に反映させることができます。
難しいことを言いましたが、ざっくり言うと「必要な部分を必要な時だけ変更するために監視する」ことを目的とした機能です。
「コンポーネント間での値の受け渡し」??
さて、コンポーネントとはコードを分離して管理するということを聞くと一つの疑問が生まれないでしょうか?
別のファイルで作った変数使いたい時どうすんの?
例えばボタンを押したときに表示される数字が一ずつ増えるコードを書いたとしましょう。そして「数字が表示される部分」をコンポーネントとして分離したとします。
別のファイルで作成・更新された変数をどうやって反映させれば良いのでしょうか...
ここで状態管理が活きてくるわけです。
import React from 'react';
export const CountViewComponent = () => {
return <p>ここに数字を表示したい</p>;
}
import React from 'react';
import { CountViewComponent } from 'countVIew.tsx';
export const IndexComponent = () => {
let num = 0;
const click = () => {
num += 1; // ここで数字を足しても、他のコンポーネントに届けられない!!
}
return (
<div>
<CountViewComponent /> /** このコンポーネントに変数numをどうにか渡したい */
<button type="button" onClick={click()}>UP</button>
</div>
);
}
そして、状態管理に役立つのがHooksというReactの機能です。
React Hooks
React HooksというのはReactの機能である状態管理及びライフサイクルを関数コンポーネントでも扱えるように開発されたv16.8からの新機能になります。
「関数コンポーネント」とは?という件については本筋から逸れるため詳細は省略します。簡単にいうと「従来のクラスコンポーネントというものよりシンプルに記載ができるけど、シンプルすぎて困る点もあった」という感じです。その困る点の解決策がHooksというわけですね。このHooksの誕生により現在では関数コンポーネントの利用が主流となりました。
ではどのようなものがあるのでしょうか?という話になるのですが、全部話すと長くなるのでよく使う三つについて説明します。もっと知りたい方はReact公式ドキュメントを読むと良いです。
useState
状態管理を行うHooksになります。
useStateは現在の値とその値を更新する関数をペアとして返します。分割代入を用いて任意の変数名をつけて扱うことができます。
関数を用いて値を更新すると、その変数を用いた描画が更新されます。
import React, { useState } from 'react';
function Example() {
// 引数は初期値
const [count, setCount] = useState(0);
return (
<div>
<p>{count}回押したよ</p>
<button onClick={() => setCount(count + 1)}>
ボタン
</button>
</div>
);
}
useEffect
ライフサイクルメソッドの代わりとなるHooksになります。
ライフサイクルメソッドとは、コンポーネントの描画開始時や完了後、更新時などのコンポーネントという生き物が生まれてから破棄されるまでの各過程に応じて発火する関数を指します。
useEffectは第一引数に発火したい処理を記載し、第二引数に依存する変数の配列を記載します。
第二引数を空の配列にするとコンポーネントのDOM作成時に発火し、変数を格納するとDOM作成時と格納された変数の変更を検知した時に発火します。
この「検知する」という現象をReactでは「変更によって起きた事柄」と捉え「副作用」と度々呼びます。
今回は状態管理の話なのであまり関係ないですが、かなり多用するので覚えておいて損はないです。
import React, { useEffect } from 'react';
function Example() {
useEffct(() => {
console.log('処理をここに記載')
}, [])
return (
<div>
<p>test</p>
</div>
);
}
useContext
useStateを拡張し、いわゆるグローバル変数のように扱えるようにするHooksになります。
useContextはuseStateと同じように「状態」と「状態の更新を行う関数」を返します。
useStateとの違いはContextという空間に状態を切り出すことができる点にあります。useStateは仕組み上同一のコンポーネント内でしか状態管理を行えません。そのため別のコンポーネントで値を利用する場合は子コンポーネントに引数として渡す以外のことはできないのです。
そこでContextを使いコンポーネントの外で状態を管理させることでコンポーネントの関係に寄らずに値を持ち回すことができるようになります。
import React, { useContext } from 'react';
import { TestContext } fron 'context.tsx';
function Example() {
const context = useContext(TestContext);
return (
<div>
<p>{count}回押したよ</p>
<button onClick={() => context.updateData(context.data + 1)}>
ボタン
</button>
</div>
);
}
import React, { createContext, useCallback } from 'react';
// Contextの作成
export const TestContext = createContext({
data: 0,
updateData: (data: number) => {},
});
// Contextの設定(カスタムフック)
export function useTestContext(): {
data: number;
updateData: (data: number) => void;
} {
const [data, setData] = useState(0);
const updateData = useCallback((data: number): void => {
setData(data);
}, []);
return {
data,
updateData,
};
}
コンポーネント間の値受け渡し
さて、ここまで読めばお分かりかと思いますが、useContextをうまく活用すればコンポーネント間で値を受け回すことができます。
コンポーネント内部ではなく、外部に値を保持させることでコンポーネント間でも値の取得・更新が可能になるわけです。
Reactではコンポーネント間の値受け渡しは「子コンポーネントの引数として渡す」「Hooksを用いて状態管理をしているところを参照する」という二つのやり方が存在するわけです。
同じような考え方で外部にHooksを使った処理を持たせるためにReactにはカスタムフックという機能もあります。少し難しい機能ですが、「複数のHooksを用いた処理を別ファイルに独立させる」と考えると紐解きやすいかもしれません。
最後に
今回記載したサンプルコードはかなり簡略化しているため、実際に記述する際はReact公式ドキュメントを参考にしましょう。結局一番分かりやすいです。
状態管理には「Redux」「Recoil」といった専用のライブラリも存在します。特にReduxには最近Redux Hooksというものが実装され、人気を博しています。
ある程度React Hooksに慣れてきたらこちらも勉強するとよりシンプルかつ効果的に状態管理を行えます。日々勉強ですね。
React Hooksは独特な考え方や使い方のためとっつきにくいところがありますが、紐解いてみるととてもシンプルな機能になっています。ライブラリも含めれば無数のHooksが存在するので、皆様もぜひ勉強してみてはいかがでしょうか?
参考サイト様
https://tyotto-good.com/blog/usestate-pitfalls
https://sbfl.net/blog/2019/02/09/react-hooks-usestate/
https://blog.uhy.ooo/entry/2021-07-24/react-state-management/
https://uncle-javascript.com/react-context-beginning
https://zenn.dev/gagaga/articles/state-management