反応中の化合物成分
公開: 2022-03-10複合コンポーネントは、開発者がコンポーネント内で状態とロジックを共有するためのより表現力豊かで柔軟なAPIを構築するのに役立ちます。 このチュートリアルでは、Context APIとReactを使用して、この高度なパターンを使用してコンポーネントを構築することで、これを実現する方法について説明します。
注:フォローできるようにするには、Reactの基本的な理解とContextAPIの動作について理解する必要があります。
複合成分とは何ですか?
複合コンポーネントは、コンポーネントのグループの状態と動作を囲みながら、その可変部分のレンダリング制御を外部ユーザーに戻すパターンであると言えます。
上記の定義から、キーワードに注意してください:状態と動作。 これは、複合コンポーネントが状態を処理すること(つまり、コンポーネントの親である外部ユーザーによって囲まれているコンポーネント全体で状態がどのように動作するか)を理解するのに役立ちます。
複合コンポーネントの目的は、親コンポーネントと子コンポーネント間の通信のためのより表現力豊かで柔軟なAPIを提供することです。
HTMLの<select>
>タグと<option>
タグのように考えてください。
<select> <option value="volvo">Volvo</option> <option value="mercedes">Mercedes</option> <option value="audi">Audi</option> </select>
select
タグは、HTMLの項目を選択するためのドロップダウンメニューに使用されるoption
タグと連携して機能します。 ここで、 <select>
はUIの状態を管理し、 <option>
要素は<select>
がどのように機能するかを構成します。 Reactの複合コンポーネントは、プロップドリルを回避するのに役立つ宣言型UIコンポーネントを構築するために使用されます。
小道具の掘削とは、複数の子コンポーネントに小道具を渡すことです。 これは彼らが「コードの臭い」と呼んでいるものでもあります。 プロップドリルの最悪の部分は、親コンポーネントが再レンダリングされると、子コンポーネントも再レンダリングされ、コンポーネントにドミノ効果が発生することです。 良い解決策は、後で調べるReactContextAPIを使用することです。
Reactでの複合コンポーネントの適用
このセクションでは、Reactでコンポーネントを構築する複合コンポーネントパターンを採用するアプリケーションで使用できるパッケージについて説明します。 この例は、 @reach
パッケージのMenu
コンポーネントです。
import { Menu, MenuList, MenuButton, MenuItem, MenuItems, MenuPopover, MenuLink, } from "@reach/menu-button"; import "@reach/menu-button/styles.css";
Menu
コンポーネントを使用する方法は次のとおりです。
function Example() { return ( <Menu> <MenuButton>Actions</MenuButton> <MenuList> <MenuItem>Download</MenuItem> <MenuLink to="view">View</MenuLink> </MenuList> </Menu> ); }
上記のサンプルコードは、 Menu
、 MenuButton
、 MenuList
、 MenuItem
、 MenuLink
がすべて@reach/menu-button
からインポートされたことを確認できる複合コンポーネントの実装の1つです。 単一のコンポーネントをエクスポートするのとは対照的に、ReachUIは、 MenuButton
、 MenuList
、 MenuItem
、およびMenuLink
である子コンポーネントに付随するMenu
である親コンポーネントをエクスポートします。
複合コンポーネントをいつ使用する必要がありますか?
React開発者は、次の場合に複合コンポーネントを使用する必要があります。
- 再利用可能なコンポーネントの構築に関連する問題を解決します。
- 最小結合による高凝集性コンポーネントの開発。
- コンポーネント間でロジックを共有するためのより良い方法。
複合コンポーネントの長所と短所
複合コンポーネントは、React開発者ツールキットに追加する素晴らしいReactパターンです。 このセクションでは、複合コンポーネントを使用することの長所と短所、およびこの開発パターンを使用してコンポーネントを構築することから学んだことを説明します。
長所
関心の分離
親コンポーネントにすべてのUI状態ロジックがあり、それをすべての子コンポーネントに内部的に伝達することで、責任を明確に分割できます。複雑さの軽減
プロパティを特定のコンポーネントに渡すためのプロップドリルとは対照的に、子プロップは複合コンポーネントパターンを使用してそれぞれの子コンポーネントに移動します。
短所
React with複合コンポーネントパターンでコンポーネントを構築することの主な短所の1つは、親コンポーネントのdirect children
のみが小道具にアクセスできることです。つまり、これらのコンポーネントを別のコンポーネントでラップすることはできません。
export default function FlyoutMenu() { return ( <FlyOut> {/* This breaks */} <div> <FlyOut.Toggle /> <FlyOut.List> <FlyOut.Item>Edit</FlyOut.Item> <FlyOut.Item>Delete</FlyOut.Item> </FlyOut.List> </div> </FlyOut> ); }
この問題の解決策は、柔軟な複合コンポーネントパターンを使用して、 React.createContext
を使用して状態を暗黙的に共有することです。
Context APIを使用すると、Reactでコンポーネントを構築する複合コンポーネントパターンを使用して構築するときに、ネストされたコンポーネントを介してReact状態を渡すことができます。 これが可能なのは、 context
が、すべてのレベルで小道具を手動で渡す必要なしに、コンポーネントツリーにデータを渡す方法を提供するためです。 Context APIを利用することで、エンドユーザーに多くの柔軟性がもたらされます。
反応中の化合物成分の維持
複合コンポーネントは、Reactアプリケーション内で状態を共有するためのより柔軟な方法を提供するため、Reactアプリケーションで複合コンポーネントを利用すると、アプリの保守と実際のデバッグが容易になります。
デモの作成
この記事では、複合コンポーネントパターンを使用してReactでアコーディオンコンポーネントを構築します。 このチュートリアルで構築するコンポーネントは、柔軟性があり、ContextAPIを使用してコンポーネント内で状態を共有するカスタムメイドのアコーディオンコンポーネントになります。
さあ行こう!
まず、以下を使用してReactアプリを作成しましょう。
npx create-react-app accordionComponent cd accordionComponent npm start
また
yarn create react-app accordionComponent cd accordionComponent yarn start
上記のコマンドは、Reactアプリを作成し、ディレクトリをReactプロジェクトに変更して、開発サーバーを起動します。
注:このチュートリアルでは、 styled-components
を使用してコンポーネントのスタイルを設定します。
以下のコマンドを使用して、 styled-components
をインストールします。
yarn add styled-components
また
npm install --save styled-components
srcフォルダーに、 componentsという名前の新しいフォルダーを作成します。 これは、すべてのコンポーネントが存在する場所です。 componentsフォルダー内に、 accordion.js
とaccordion.styles.js
の2つの新しいファイルを作成します。
accordion.styles.js
ファイルには、 Accordion
コンポーネントのスタイリングが含まれています(スタイリングはstyled-components
を使用して行われました)。
import styled from "styled-components"; export const Container = styled.div` display: flex; border-bottom: 8px solid #222; `;
上記は、 styled-components
と呼ばれるcss-in-js
ライブラリを使用したコンポーネントのスタイリングの例です。
accordion.styles.js
ファイル内に、残りのスタイルを追加します。
export const Frame = styled.div` margin-bottom: 40px; `; export const Inner = styled.div` display: flex; padding: 70px 45px; flex-direction: column; max-width: 815px; margin: auto; `; export const Title = styled.h1` font-size: 40px; line-height: 1.1; margin-top: 0; margin-bottom: 8px; color: black; text-align: center; `; export const Item = styled.div` color: white; margin: auto; margin-bottom: 10px; max-width: 728px; width: 100%; &:first-of-type { margin-top: 3em; } &:last-of-type { margin-bottom: 0; } `; export const Header = styled.div` display: flex; flex-direction: space-between; cursor: pointer; margin-bottom: 1px; font-size: 26px; font-weight: normal; background: #303030; padding: 0.8em 1.2em 0.8em 1.2em; user-select: none; align-items: center; img { filter: brightness(0) invert(1); width: 24px; user-select: none; @media (max-width: 600px) { width: 16px; } } `; export const Body = styled.div` font-size: 26px; font-weight: normal; line-height: normal; background: #303030; white-space: pre-wrap; user-select: none; overflow: hidden; &.closed { max-height: 0; overflow: hidden; transition: max-height 0.25ms cubic-bezier(0.5, 0, 0.1, 1); } &.open { max-height: 0px; transition: max-height 0.25ms cubic-bezier(0.5, 0, 0.1, 1); } span { display: block; padding: 0.8em 2.2em 0.8em 1.2em; } `;
アコーディオンコンポーネントの作成を始めましょう。 accordion.js
ファイルに、次のコードを追加しましょう。
import React, { useState, useContext, createContext } from "react"; import { Container, Inner, Item, Body, Frame, Title, Header } from "./accordion.styles";
上記では、 useState
、 useContext
、およびcreateContext
フックをインポートしています。これは、複合コンポーネントを使用してアコーディオンコンポーネントを構築するのに役立ちます。
Reactのドキュメントでは、 context
は、すべてのレベルで小道具を手動で渡す必要なしに、コンポーネントツリーを介してデータを渡す方法を提供するのに役立つと説明しています。
以前にaccordion.js
ファイルにインポートしたものを見ると、コンポーネントをコンポーネントとしてインポートしたことにも気付くでしょう。これにより、コンポーネントをより高速に構築できます。
先に進み、コンポーネントを必要とするコンポーネントとデータを共有するコンポーネントのコンテキストを作成します。
const ToggleContext = createContext(); export default function Accordion({ children, ...restProps }) { return ( <Container {...restProps}> <Inner>{children}</Inner> </Container> ); }
上記のコードスニペットのContainer
コンポーネントとInner
コンポーネントは、( css-in-js
ライブラリの) styled-components
を使用してコンポーネントのスタイルを作成した./accordion.styles.js
ファイルからのものです。 Container
コンポーネントは、複合コンポーネントを使用して構築しているAccordion
全体を収容します。
ここでは、 createContext()
メソッドを使用してコンテキストオブジェクトを作成しているため、ReactがこのContextオブジェクトにサブスクライブするコンポーネントをレンダリングすると、ツリー内でその上にある最も近いプロバイダーから現在のコンテキスト値を読み取ります。
次に、アコーディオンである基本コンポーネントも作成しています。 children
とrestProps
が必要です。 これは、アコーディオンの子コンポーネントを格納する親コンポーネントです。
accordion.js
ファイル内に他の子コンポーネントを作成しましょう。
Accordion.Title = function AccordionTitle({ children, ...restProps }) { return <Title {...restProps}>{children}</Title>; }; Accordion.Frame = function AccordionFrame({ children, ...restProps }) { return <Frame {...restProps}>{children}</Frame>; };
に注意して.
親アコーディオンコンポーネントの後。 これは、子コンポーネントをその親コンポーネントに接続するために使用されます。
続けましょう。 次に、 accordion.js
ファイルに以下を追加します。
Accordion.Item = function AccordionItem({ children, ...restProps }) { const [toggleShow, setToggleShow] = useState(true); return ( <ToggleContext.Provider value={{ toggleShow, setToggleShow }}> <Item {...restProps}>{children}</Item> </ToggleContext.Provider> ); }; Accordion.ItemHeader = function AccordionHeader({ children, ...restProps }) { const { isShown, toggleIsShown } = useContext(ToggleContext); return ( <Header onClick={() => toggleIsShown(!isShown)} {...restProps}> {children} </Header> ); }; Accordion.Body = function AccordionHeader({ children, ...restProps }) { const { isShown } = useContext(ToggleContext); return ( <Body className={isShown ? "open" : "close"}> <span>{children}</span> </Body> ); };
したがって、ここでは、すべて親コンポーネントのAccordion
の子であるBody
、 Header
、およびItem
コンポーネントを作成しています。 これは、それがトリッキーになり始めるかもしれないところです。 また、ここで作成された各子コンポーネントは、 children
プロップとrestprops
プロップも受け取ることに注意してください。
Item
子コンポーネントから、 useState
フックを使用して状態を初期化し、trueに設定しました。 次に、 Context Object
であるaccordion.js
ファイルのトップレベルでToggleContext
を作成したことも思い出してください。また、Reactがこのコンテキストオブジェクトにサブスクライブするコンポーネントをレンダリングすると、その上にある最も近いプロバイダーから現在のコンテキスト値が読み取られます。木の中。
すべてのContextオブジェクトには、消費コンポーネントがコンテキスト変更をサブスクライブできるようにするProvider
コンポーネントが付属しています。
provider
コンポーネントは、このプロバイダーの子孫である消費コンポーネントに渡されるvalue
propを受け入れます。ここでは、現在の状態の値setToggleShowを設定するためのtoggleShow
およびメソッドである現在の状態の値をsetToggleShow
ます。 これらは、プロップドリルを使用せずにコンテキストオブジェクトがコンポーネントの周囲で状態を共有する方法を決定する値です。
次に、 Accordion
のheader
コンポーネントで、コンテキストオブジェクトの値を破棄し、クリック時にtoggleShow
の現在の状態を変更します。 したがって、私たちがやろうとしているのは、ヘッダーがクリックされたときにアコーディオンを非表示または表示することです。
Accordion.Body
コンポーネントでは、コンポーネントの現在の状態であるtoggleShow
も破棄しています。次に、 toggleShow
の値に応じて、本体を非表示にするか、 Accordion.Body
コンポーネントの内容を表示することができます。
これで、 accordion.js
ファイルは以上です。
ここで、 Context
コンポーネントとCompound components
について学んだすべてがどのように組み合わされるかを確認できます。 ただし、その前に、 data.json
という名前の新しいファイルを作成し、その中に以下のコンテンツを貼り付けましょう。
[ { "id": 1, "header": "What is Netflix?", "body": "Netflix is a streaming service that offers a wide variety of award-winning TV programs, films, anime, documentaries and more – on thousands of internet-connected devices.\n\nYou can watch as much as you want, whenever you want, without a single advert – all for one low monthly price. There's always something new to discover, and new TV programs and films are added every week!" }, { "id": 2, "header": "How much does Netflix cost?", "body": "Watch Netflix on your smartphone, tablet, smart TV, laptop or streaming device, all for one low fixed monthly fee. Plans start from £5.99 a month. No extra costs or contracts." }, { "id": 3, "header": "Where can I watch?", "body": "Watch anywhere, anytime, on an unlimited number of devices. Sign in with your Netflix account to watch instantly on the web at netflix.com from your personal computer or on any internet-connected device that offers the Netflix app, including smart TVs, smartphones, tablets, streaming media players and game consoles.\n\nYou can also download your favorite programs with the iOS, Android, or Windows 10 app. Use downloads to watch while you're on the go and without an internet connection. Take Netflix with you anywhere." }, { "id": 4, "header": "How do I cancel?", "body": "Netflix is flexible. There are no annoying contracts and no commitments. You can easily cancel your account online with two clicks. There are no cancellation fees – start or stop your account at any time." }, { "id": 5, "header": "What can I watch on Netflix?", "body": "Netflix has an extensive library of feature films, documentaries, TV programs, anime, award-winning Netflix originals, and more. Watch as much as you want, any time you want." } ]
これは、アコーディオンコンポーネントをテストするために使用するデータです。
それでは、続けましょう。 ほぼ完了しました。この記事をフォローすることで、多くのことを学んだと思います。
このセクションでは、これまで取り組んできたすべてのことをまとめ、複合コンポーネントについて学習して、 App.js
ファイルで使用し、 Array.map
関数を使用してWeb上にすでにあるデータを表示できるようにします。ページ。 また、 App.js
内で状態が使用されていないことにも注意してください。 私たちが行ったのは、特定のコンポーネントにデータを渡すことだけで、ContextAPIが他のすべてのことを処理しました。
最後の部分に移ります。 App.js
で、次の手順を実行します。
import React from "react"; import Accordion from "./components/Accordion"; import faqData from "./data"; export default function App() { return ( <Accordion> <Accordion.Title>Frequently Asked Questions</Accordion.Title> <Accordion.Frame> {faqData.map((item) => ( <Accordion.Item key={item.id}> <Accordion.Header>{item.header}</Accordion.Header> <Accordion.Body>{item.body}</Accordion.Body> </Accordion.Item> ))} </Accordion.Frame> </Accordion> ); }
App.jsファイルでは、ファイルパスからCompound Component Accordionをインポートし、次にダミーデータもインポートし、データファイル内の個々のアイテムを取得するためにダミーデータを介してマッピングし、それぞれに従って表示しました。コンポーネント、また、私たちがしなければならなかったのは、子をそれぞれのコンポーネントに渡すことだけでした。ContextAPIは、適切なコンポーネントに到達し、支柱のドリルがないことを確認します。
これが私たちの最終製品がどのように見えるべきかです:
複合コンポーネントの代替
複合コンポーネントを使用する代わりに、RenderPropsAPIを使用することもできます。 Reactでプロップをレンダリングするという用語は、値が関数であるプロップを使用してReactコンポーネント間でコードを共有するための手法を指します。 レンダープロップを備えたコンポーネントは、独自のレンダーロジックを実装する代わりに、React要素を返す関数を受け取り、それを呼び出します。
コンポーネントからデータを必要とする子コンポーネントにデータを渡すと、コンポーネントが相互にネストされている場合にプロップドリルが発生する可能性があります。 これは、レンダリングプロップメソッドを使用するよりも、コンテキストを使用してコンポーネント間でデータを共有することの利点です。
結論
この記事では、複合コンポーネントパターンであるReactの高度なパターンの1つについて学びました。 複合コンポーネントパターンを使用してコンポーネントを構築することにより、Reactで再利用可能なコンポーネントを構築するための優れた方法であり、コンポーネントに多くの柔軟性を提供します。 現時点でコンポーネントに必要な柔軟性がない場合でも、レンダリングプロップを使用することを選択できます。
複合コンポーネントは、設計システムの構築に最も役立ちます。 また、ContextAPIを使用してコンポーネント内で状態を共有するプロセスも実行しました。
- このチュートリアルのコードは、Codesandboxにあります。