- vừa được xem lúc

Blog#96: Reactをマスターする:すべての開発者に必要な5つの基本概念

0 0 20

Người đăng: NGUYỄN ANH TUẤN

Theo Viblo Asia

こんにちは、私は東京からのフルスタック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>} </> ); }

これは、selectedUseruserIdから直接導出され、別の状態変数として追跡されないため機能します。これは、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フレームワークで状態や効果がどのように動作するかを理解するためにも重要です。

いつものように、この記事を楽しんで新しいことを学んでいただけたと思います。

ありがとうございました。次の記事でお会いしましょう!

この記事が気に入ったら、いいねをして購読してサポートしてください。ありがとうございます。

Ref

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 525

- vừa được xem lúc

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 433

- vừa được xem lúc

Một số phương thức với object trong Javascript

Trong Javascript có hỗ trợ các loại dữ liệu cơ bản là giống với hầu hết những ngôn ngữ lập trình khác. Bài viết này mình sẽ giới thiệu về Object và một số phương thức thường dùng với nó.

0 0 153

- vừa được xem lúc

Tìm hiểu về thư viện axios

Giới thiệu. Axios là gì? Axios là một thư viện HTTP Client dựa trên Promise.

0 0 145

- vừa được xem lúc

Imports và Exports trong JavaScript ES6

. Giới thiệu. ES6 cung cấp cho chúng ta import (nhập), export (xuất) các functions, biến từ module này sang module khác và sử dụng nó trong các file khác.

0 0 110

- vừa được xem lúc

Bài toán đọc số thành chữ (phần 2) - Hoàn chỉnh chương trình dưới 100 dòng code

Tiếp tục bài viết còn dang dở ở phần trước Phân tích bài toán đọc số thành chữ (phần 1) - Phân tích đề và những mảnh ghép đầu tiên. Bạn nào chưa đọc thì có thể xem ở link trên trước nhé.

0 0 245