こんにちは、私は東京からのフルスタックWebデベロッパーであるTUANです。
今後の便利で面白い記事を見逃さないように、私のブログをフォローしてください。
React
は簡単に学べる、人気のあるフロントエンドのライブラリです。しかし、高速かつパフォーマントなコードを書くためには開発者はいくつかの概念を理解する必要があります。この記事では、React
での状態とエフェクトの仕組みに関連する注意点と概念を詳しく説明します。
状態を導出する
時には、別の状態変数に依存する状態変数を作成する必要があります。それを行う方法の1つは、useEffect
フックを使用して依存する状態変数を更新することです。 例えば、ユーザーIDのリストを表示するセレクト要素があるとします。選択されたユーザーIDをuserId
状態変数で追跡したいと考えます。そうするためには、以下のようにします:
import { useState } from "react"
const users = [
{ id: "1", name: "User One" },
{ id: "2", name: "User Two" },
{ id: "3", name: "User Three" },
]
function Users () {
const [userId, setUserId] = useState("1")
return(
<select value={userId} onChange={e => setUserId(e.target.value)}>
<option value="1">User One</option>
<option value="2">User Two</option>
<option value="3">User Three</option>
</select>
);
}
今、選択したユーザーを画面に表示したいとします。selectedUser
という別の状態変数を作成し、userId
が変わったときにuseEffect
フックを使用してそれを更新することができます。
import { useState, useEffect } from "react"
function Users () {
const [userId, setUserId] = useState("")
const [selectedUser, setSelectedUser] = useState(undefined)
useEffect(() => {
setSelectedUser(users.find(u => u.id === userId))
}, [userId])
return(
<>
<select value={userId} onChange={e => setUserId(e.target.value)}>
<option>Select a user</option>
<option value="1">User One</option>
<option value="2">User Two</option>
<option value="3">User Three</option>
</select>
{selectedUser && <p>The selected user is: {selectedUser.name}</p>}
</>
);
}
このアプローチは機能しますが、selectedUser
を更新するための最も効率的な方法ではありません。この方法の問題は、コンポーネントがuserId
が変更されたときに最初にレンダーされ、その後にuseEffect
フックがトリガーされるため、userId
が依存配列に渡されるためです。これは、selectedUser
を更新するためにコンポーネントが2回レンダリングされることを意味して、最初にuserId
が変更されたとき、そして2回目にuseEffect
フックがselectedUser
を更新したときです。
もっと良いアプローチは、selectedUserの値をuserIdから直接導出することです。
function Users () {
const [userId, setUserId] = useState("")
const selectedUser = users.find(u => u.id === userId)
return(
<>
<select value={userId} onChange={e => setUserId(e.target.value)}>
<option>Select a user</option>
<option value="1">User One</option>
<option value="2">User Two</option>
<option value="3">User Three</option>
</select>
{selectedUser && <p>The selected user is: {selectedUser.name}</p>}
</>
);
}
これは、selectedUser
がuserId
から直接導出され、別の状態変数として追跡されないため機能します。これは、userId
が変更されたときだけコンポーネントが再レンダリングされることを意味し、前の例のように2回再レンダリングすることはありません。
イベントハンドラー内で状態をセット
時には、クリックイベントやフォーム提出イベントなどのイベントハンドラー内で状態を更新する必要があります。これらの場合、状態更新が非同期に実行されることを確認することが重要で、イベントハンドラーが実行を終える前にコンポーネントが再レンダリングする機会を持つことができるようにします。
イベントハンドラー内で状態を正しく設定する方法の例:
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>The count is: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
この例では、クリックしたときにカウントをインクリメントするボタンがある「Counter
」というコンポーネントがあり、カウントを表示します。 handleClick
イベントハンドラーは、setCount
関数を使用してcount
状態変数を更新します。
setCount
関数が非同期であることに注意することが重要で、つまり状態更新はすぐにコンポーネントに反映されない可能性があります。代わりに、イベントハンドラーが実行を終えると、コンポーネントは再レンダリングされます。
現在の状態の値に依存する操作を実行する必要がある場合は、setCount
関数に関数を渡すことができます。例えば:
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount((currentCount) => currentCount + 1);
};
return (
<div>
<p>The count is: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
この例では、setCount
関数は現在のcount
の値を引数として、新しい値を返す関数を渡して呼び出されます。これは、コンポーネントが再レンダリングする機会がなくても、状態更新が正しく実行されることを保証します。
クリーンアップ関数
時には、コンポーネントがアンマウントされたり、ある条件がもう満たされなくなった場合にクリーンアップアクションを実行する必要があります。これらの場合には、useEffect
フックとともにクリーンアップ関数を使用することができます。
クリーンアップ関数は、コンポーネントがアンマウントされたり、useEffect
フックの依存関係が変更された場合に呼び出される関数です。ここは、ネットワークリクエストのキャンセルやイベントリスナーの削除などの必要なクリーンアップを行う良い場所です。
useEffect
フックとともにクリーンアップ関数を使用する方法の例:
function Users() {
const [userId, setUserId] = useState(1);
const [user, setUser] = useState(null);
useEffect(() => {
const fetchUser = async () => {
const response = await fetch(`/users/${userId}`);
const data = await response.json();
setUser(data);
};
fetchUser();
return () => {
// Cleanup function
console.log("Cleaning up...");
};
}, [userId]);
return (
<div>
{user ? (
<p>The user is: {user.name}</p>
) : (
<p>Loading...</p>
)}
<button onClick={() => setUserId(2)}>Load User 2</button>
</div>
);
}
この例では、userId
状態変数に基づいてバックエンドAPIからユーザーを取得するUsers
というコンポーネントがあります。userId
が変更されたときにフェッチをトリガーするためにuseEffect
フックが使用されています。
useEffect
フックには、コンポーネントがアンマウントされたり、userId
状態変数が変更された場合に呼び出されるクリーンアップ関数もあります。この場合、クリーンアップ関数は単にコンソールにメッセージをログします。
重要なのは、クリーンアップ関数はuseEffect
フックの依存関係が変更されたり、コンポーネントがアンマウントされた場合にのみ呼び出されることです。これは、userId
状態変数が変更されてもuseEffect
フックの依存関係が変更されない場合、クリーンアップ関数は呼び出されないことを意味しています。
プロップが変更されたときに状態を更新
時には、コンポーネントに渡されるプロップに基づいてコンポーネントの状態を更新する必要があります。これらの場合には、useEffect
フックを使用して、プロップが変更されたときを検出し、それに応じて状態を更新することができます。
プロップが変更されたときに状態を更新する方法の例:
function User({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
const fetchUser = async () => {
const response = await fetch(`/users/${userId}`);
const data = await response.json();
setUser(data);
};
fetchUser();
}, [userId]);
return user ? <p>The user is: {user.name}</p> : <p>Loading...</p>;
}
この例では、userId
プロップに基づいてバックエンドAPIからユーザーを取得する「User
」というコンポーネントがあります。 userId
プロップが変更されたときにフェッチをトリガーするためにuseEffect
フックが使用されています。
重要なのは、useEffectフックの依存関係配列には、エフェクトが依存するプロップや状態変数を含める必要があることです。この場合、エフェクトはuserId
プロップに依存しているため、依存関係配列に含まれています。
useEffect
フックの依存関係が正しく指定されていない場合、エフェクトが不必要にトリガーされる可能性があり、パフォーマンスの問題を引き起こす可能性があります。
同じ位置で状態を保存
時には、同じ位置でページを再描画するときにコンポーネントの状態を保持する必要があります。これらの場合は、useState
フックとuseMemo
フックを使用して状態を保存できます。
useMemo
フックは、依存関係が変更された場合にのみ再計算されるように値をメモ化することができるパフォーマンス最適化です。これは、同じ位置で再レンダリングされたときにコンポーネントの状態を保持するために便利です。
useMemo
フックを使用してコンポーネントの状態を保存する方法の例:
function Counter({ initialCount }) {
const [count, setCount] = useState(initialCount);
const increment = useMemo(() => {
return () => setCount(count + 1);
}, [count]);
return (
<div>
<p>The count is: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
この例では、「カウンタ」というコンポーネントがあり、クリックしたときにカウントを増やすボタンがあり、カウントが表示されています。「increment
」関数は、useMemo
フックを使用して作成され、count
状態変数が変更された場合にのみ再計算されます。これは、「increment
」関数が常に「Counter
」コンポーネントが同じ位置で再描画された場合でも「count
」の正しい値を持つことを意味します。
重要なのは、useMemo
フックの依存関係配列には、メモ化された値が依存するプロップや状態変数を含める必要があることです。この場合、「increment
」関数はcount
状態変数に依存しているため、依存関係配列に含まれています。
useMemo
フックの依存関係が正しく指定されていない場合、メモ化された値が不必要に再計算される可能性があり、パフォーマンスの問題を引き起こす可能性があります。
結論
React
における状態とエフェクトに関連したいくつかの概念を話しました。状態を派生すること、イベントハンドラ内で状態を設定すること、useEffectフックとクリーンアップ関数を使用すること、プロップが変更されたときに状態を更新すること、そして同じ位置で再レンダリングされたときにコンポーネントの状態を保持することなどについてカバーしました。
これらの概念の理解は、Reactで効率的でパフォーマントなコードを書くために重要であり、またReactフレームワークで状態や効果がどのように動作するかを理解するためにも重要です。
いつものように、この記事を楽しんで新しいことを学んでいただけたと思います。
ありがとうございました。次の記事でお会いしましょう!
この記事が気に入ったら、いいねをして購読してサポートしてください。ありがとうございます。