MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
Reactの基礎 - リストレンダリングのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
Reactの基礎 - リストレンダリング
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == Reactにおけるリストレンダリングとは、配列データをもとに複数のTSX要素を動的に生成して表示する技術である。<br> <br> TypeScriptの <code>map()</code> 関数を使用することにより、配列の各要素をTSX要素に変換して画面に表示できる。<br> <br> リストレンダリングを定義する場合は、各要素に <code>key</code> 属性を付与することが必須となる。<br> <code>key</code> 属性は、ReactがどのコンポーネントIDが変化したかを識別するために使用する特別な属性であり、適切に設定することでパフォーマンスの最適化とバグの防止につながる。<br> <br> また、<code>filter()</code> 関数と組み合わせることで条件付きのリストレンダリングを実現でき、入れ子の配列を扱うネストしたリストも実装できる。<br> <br> 下表に、リストレンダリングの手法を示す。<br> <br> <center> {| class="wikitable" |+ リストレンダリングの手法 |- ! テクニック |- | <code>map()</code> を使用した配列要素のTSX変換 |- | <code>key</code> 属性によるコンポーネント同一性の管理 |- | <code>filter()</code> との組み合わせによる条件付き表示 |- | ネストした配列の再帰的なレンダリング |- | 大規模リストに対する仮想化ライブラリの活用 |} </center> <br><br> == map() による繰り返し描画 == TypeScriptの <code>map()</code> 関数は、配列の各要素を変換して新しい配列を返す。<br> <br> Reactでは、この特性を利用して配列データをTSX要素の配列に変換して、コンポーネントとして描画する。<br> <br> ==== リストレンダリングの基本 ==== 文字列配列を <code>map()</code> でリスト要素に変換する基本的な例を以下に示す。<br> <br> <syntaxhighlight lang="typescript"> const people: string[] = ['Alice', 'Bob', 'Charlie']; export default function List() { const listItems = people.map(person => <li key={person}>{person}</li> ); return <ul>{listItems}</ul>; } </syntaxhighlight> <br> 上記の例では、<u>people</u> 配列の各文字列を <u><li></u> 要素に変換している。<br> 文字列配列の場合は、値自体が一意であれば <u>key</u> 属性の値として使用できる。<br> <br> 数値配列の場合も同様に、<code>map()</code> で各数値をリスト要素に変換できる。<br> <br> <syntaxhighlight lang="typescript"> const numbers: number[] = [1, 2, 3, 4, 5]; export default function NumberList() { const listItems = numbers.map(number => <li key={number}>{number}</li> ); return <ul>{listItems}</ul>; } </syntaxhighlight> <br><br> ==== オブジェクト配列のレンダリング ==== 実際のアプリケーションでは、ユーザ情報等のオブジェクト配列を扱うことが多い。<br> <br> オブジェクト配列のレンダリング例を以下に示す。<br> <br> <syntaxhighlight lang="typescript"> interface Person { id: number; name: string; profession: string; } const people: Person[] = [ { id: 0, name: 'Creola Katherine Johnson', profession: 'mathematician' }, { id: 1, name: 'Mario José Molina-Pasquel', profession: 'chemist' }, { id: 2, name: 'Mohammad Abdus Salam', profession: 'physicist' }, ]; export default function List() { const listItems = people.map(person => <li key={person.id}> <p>{person.name}: {person.profession}</p> </li> ); return <ul>{listItems}</ul>; } </syntaxhighlight> <br> オブジェクト配列では、各オブジェクトが持つ一意のID (上記では、<u>person.id</u>) を <u>key</u> 属性に使用するのが最適である。<br> <code>map()</code> のコールバック関数内では、オブジェクトの各プロパティにアクセスして表示内容を構成できる。<br> <br><br> == key属性 == <code>key</code> 属性は、Reactがリストの各アイテムを識別するために使用する特別な属性である。<br> 適切な <code>key</code> を設定することで、リストの更新・並べ替え・削除を効率的に処理できる。<br> <br> ==== keyの役割 ==== <code>key</code> 属性は、Reactの差分検出アルゴリズム (Reconciliation) において重要な役割がある。<br> <br> Reconciliationとは、ReactがVirtual DOMと実際のDOMの差分を検出して、必要な箇所のみを更新するプロセスである。<br> <code>key</code> 属性の働きは以下の通りである。<br> <br> * コンポーネントの同一性識別 *: Reactは <code>key</code> の一致によってコンポーネントが同一のデータを表しているかを識別する。 *: これはファイルシステムにおけるファイル名に相当する概念であり、名前が変わればReactは別のコンポーネントと判断する。 *: <br> * 状態の保持 *: データの順序が変わっても、<code>key</code> が同じであればコンポーネントの状態 (state) が保持される。 *: <code>key</code> が無い場合は位置 (インデックス) で識別されるため、順序変更時にフォーム入力値等の状態が誤ったアイテムに紐づくバグが発生する。 *: <br> * 再マウントの制御 *: <code>key</code> が変わった場合、ReactはそのコンポーネントをアンマウントしてDOMから削除し、新しいコンポーネントとして再マウントする。 *: これにより、以前の状態は引き継がれずにリセットされる。 <br> ==== keyの選定基準 ==== 下表に、適切な <code>key</code> と不適切な <code>key</code> の基準を示す。<br> <br> <center> {| class="wikitable" |+ keyの適否 ! 種別 !! 例 !! 説明 |- | 適切 (最適) || <u>key={person.id}</u> || データベースのID等、データが持つ一意の識別子を使用する。最も推奨される方法。 |- | 適切 || <u>key={crypto.randomUUID()}</u> || ローカルで生成する場合は、インクリメントカウンタやUUIDを使用する。<br>ただし、コンポーネントの初期化時に1度だけ生成してstateに保持すること。 |- | 不適切 || <u>key={index}</u> || 配列のインデックスをkeyに使用してはならない。<br>アイテムの削除や並べ替えが発生した場合に、フォームの入力値が誤ったアイテムに紐づくバグが生じる。 |- | 絶対禁止 || <u>key={Math.random()}</u> || レンダリングのたびにkeyが変わるため、全コンポーネントとDOMが毎回再作成される。<br>パフォーマンスが著しく低下する。 |- | 禁止 || レンダリング中のkey生成 || 関数の実行のたびにkeyが変わるため、コンポーネントが毎回再マウントされてしまう。 |} </center> <br> ==== keyの制約 ==== <code>key</code> 属性には以下に示す制約がある。<br> <br> <center> {| class="wikitable" |+ key 属性に関する制約 |- ! ルール !! 説明 |- | 兄弟要素間でのみ一意であればよい | * <code>key</code> はグローバルに一意である必要はなく、同じリスト内の兄弟要素間でのみ一意であれば問題ない。 * 別のリストや別のコンポーネントツリーでは、同じ <code>key</code> 値を使用できる。 |- | keyは不変でなければならない || レンダリング中に <code>key</code> が変わってはならない。<br>不変の値のみを <code>key</code> に使用すること。 |- | keyはpropsとして受け取れない | * <code>key</code> はReact内部で使用される特別な属性であり、コンポーネントの <code>props</code> としてアクセスすることはできない。 * コンポーネントにIDを渡す場合は、<u><Profile key={id} userId={id} /></u> のように別のpropを使用して渡す。 |} </center> <br><br> == filter() との組み合わせ == <code>filter()</code> 関数は、配列の要素をある条件でフィルタリングして新しい配列を返す。<br> <code>map()</code> と組み合わせることにより、条件に合致するアイテムのみを表示できる。<br> <br> ==== 条件付きリストレンダリング ==== <code>filter()</code> と <code>map()</code> を組み合わせた条件付きレンダリングの例を以下に示す。<br> <br> <syntaxhighlight lang="typescript"> // Person型は上記で定義済み const people: Person[] = [ { id: 0, name: 'Creola Katherine Johnson', profession: 'mathematician' }, { id: 1, name: 'Mario José Molina-Pasquel', profession: 'chemist' }, { id: 2, name: 'Mohammad Abdus Salam', profession: 'physicist' }, { id: 3, name: 'Percy Lavon Julian', profession: 'chemist' }, ]; export default function List() { // chemistのみを抽出 const chemists = people.filter(person => person.profession === 'chemist' ); // 抽出した結果をTSX要素に変換 const listItems = chemists.map(person => <li key={person.id}>{person.name}</li> ); return <ul>{listItems}</ul>; } </syntaxhighlight> <br> <code>filter()</code> と <code>map()</code> はメソッドチェーンで記述することもできる。<br> <br> <syntaxhighlight lang="typescript"> const listItems = people .filter(person => person.profession === 'chemist') .map(person => <li key={person.id}>{person.name}</li>); </syntaxhighlight> <br> 条件に応じて異なるセクションを表示するコンポーネントに抽出するパターンを以下に示す。<br> <br> <syntaxhighlight lang="typescript"> interface ListSectionProps { title: string; people: Person[]; } function ListSection({ title, people }: ListSectionProps) { return ( <> <h2>{title}</h2> <ul> {people.map(person => <li key={person.id}>{person.name}</li> )} </ul> </> ); } export default function App() { const chemists = people.filter(p => p.profession === 'chemist'); const others = people.filter(p => p.profession !== 'chemist'); return ( <> <ListSection title="化学者" people={chemists} /> <ListSection title="その他" people={others} /> </> ); } </syntaxhighlight> <br><br> == ネストしたリスト == カテゴリとその配下のアイテムのように、入れ子になった配列構造をレンダリングする場合がある。<br> <u>ネストしたリストでは、外側と内側の <code>map()</code> それぞれに適切な <code>key</code> を設定する必要がある。</u><br> <br> ==== 入れ子の配列のレンダリング ==== カテゴリとアイテムを持つネストしたリストのレンダリング例を以下に示す。<br> <br> <syntaxhighlight lang="typescript"> interface Recipe { id: string; name: string; ingredients: string[]; } const recipes: Recipe[] = [ { id: 'greek-salad', name: 'Greek Salad', ingredients: ['tomatoes', 'cucumber', 'olives', 'feta'] }, { id: 'hummus', name: 'Hummus', ingredients: ['chickpeas', 'olive oil', 'garlic', 'lemon'] }, { id: 'pizza', name: 'Pizza', ingredients: ['pizza crust', 'pizza sauce', 'mozzarella'] }, ]; export default function RecipeList() { return ( <div> {recipes.map(recipe => <div key={recipe.id}> <h2>{recipe.name}</h2> <ul> {recipe.ingredients.map(ingredient => <li key={ingredient}>{ingredient}</li> )} </ul> </div> )} </div> ); } </syntaxhighlight> <br> ネストしたリストのポイントを以下に示す。<br> <br> * 外側の <code>map()</code> では <code>recipe.id</code> を <code>key</code> に使用する *: データベースIDを使用するため、最適な <code>key</code> の選択となる。 *: <br> * 内側の <code>map()</code> では <code>ingredient</code> (材料名の文字列) を <code>key</code> に使用する *: 同一レシピ内での材料名は一意であるため、文字列を <code>key</code> として使用できる。 *: <br> * 各レベルで独立したkeyスコープが存在する *: 外側リストと内側リストの <code>key</code> は独立しており、異なるレシピの内側リスト間で同じ材料名が使用されていても問題ない。 <br> また、複数の要素をラップしつつ余分なDOMノードを追加したくない場合は、<code>Fragment</code> を使用して <code>key</code> を指定できる。<br> <br> <syntaxhighlight lang="typescript"> import { Fragment } from 'react'; export default function RecipeList() { return ( <div> {recipes.map(recipe => <Fragment key={recipe.id}> <h2>{recipe.name}</h2> <ul> {recipe.ingredients.map(ingredient => <li key={ingredient}>{ingredient}</li> )} </ul> </Fragment> )} </div> ); } </syntaxhighlight> <br> 短縮構文 <code><nowiki><></nowiki></code> では <code>key</code> を指定できないため、明示的な <code>Fragment</code> コンポーネントを使用する必要がある。<br> <br><br> == リストレンダリングの注意事項 == リストレンダリングを定義する場合は、空配列の処理とパフォーマンスに関する注意が必要である。<br> <br> ==== 空配列の場合の処理 ==== <u><code>filter()</code> の結果が空配列になる場合 や データが存在しない場合の処理を適切に記述することが重要である。</u><br> <br> <syntaxhighlight lang="typescript"> interface Item { id: number; name: string; } interface FilteredListProps { items: Item[]; query: string; } export default function FilteredList({ items, query }: FilteredListProps) { const filteredItems = items.filter(item => item.name.includes(query) ); return ( <ul> {filteredItems.length > 0 ? ( filteredItems.map(item => <li key={item.id}>{item.name}</li> ) ) : ( <li>アイテムが見つかりません</li> )} </ul> ); } </syntaxhighlight> <br> 下表に、空配列の処理に関するポイントを示す。<br> <br> <center> {| class="wikitable" |+ 空配列の処理に関する注意事項 |- ! 項目 !! 説明 |- | <code>length > 0</code> による条件分岐 || 配列が空かどうかをチェックして、空の場合はフォールバックUIを表示する。<br>ユーザにとって何も表示されないよりも、"結果なし"のメッセージを表示する方が親切な設計となる。 |- | <code>&&</code> 演算子による短絡評価は避ける || <code>{items.length && items.map(...)}</code> のような記述では、items.lengthが0の場合、0がそのままレンダリングされるバグが生じる。<br>そのため、三項演算子(<code>? :</code>)を使用することを推奨する。 |} </center> <br> ==== パフォーマンスの考慮 ==== 通常のリストレンダリングは数百件程度のデータであれば問題なく動作する。<br> しかし、数千件から数万件の大規模なリストになると、全DOMノードの生成と保持によりパフォーマンスが低下する場合がある。<br> <br> <u>大規模リストに対しては仮想化 (Virtualization) の導入を検討する必要がある。</u><br> <u>仮想化とは、画面上に表示されている部分のみをDOMに描画し、スクロールに応じて描画範囲を動的に切り替える技術である。</u><br> <br> 下表に、代表的な仮想化ライブラリを示す。<br> <br> <center> {| class="wikitable" |+ Reactの仮想化ライブラリ ! ライブラリ !! 特徴 !! 推奨用途 |- | react-window || 軽量でシンプルなAPI<br>バンドルサイズが小さい。 || アイテムの高さが固定の場合 |- | react-virtuoso || 高機能で動的な高さに対応<br>グループ化やフッタ等の機能も持つ。 || アイテムの高さが動的な場合、高度な機能が必要な場合 |} </center> <br> 仮想化ライブラリの使用を検討する目安を以下に示す。<br> <br> * 100件以下のリスト *: 仮想化は不要 *: <code>map()</code> によるレンダリングで問題ない。 * 100件から1,000件程度のリスト *: ユーザの端末性能や表示内容の複雑さによって判断する。 * 1,000件以上のリスト *: 仮想化ライブラリの導入を強く推奨する。 <br><br> == 関連情報 == * [[Reactの基礎 - TSXの基本構文]] * [[Reactの基礎 - JSXの基本構文]] * [[Reactの基礎 - 条件レンダリング]] * [[Reactの基礎 - コンポーネント]] * [[Reactの基礎 - PropsとChildren]] <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板,React,JavaScript,TypeScript,TSX,JSX,リストレンダリング,map,key,filter,Reconciliation,仮想化,コンポーネント,フロントエンド |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:Rust]][[カテゴリ:Web]]
Reactの基礎 - リストレンダリング
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse