ReactでStyled-Componentsを使用する方法

公開: 2022-03-10
簡単なまとめ↬コンポーネント駆動型のアプローチは、Webアプリケーションの構築方法に新たなフロンティアをもたらしましたが、CSSでの使いやすさとスケーラビリティという欠点がないわけではありません。 これにより、コンポーネント固有の方法でスタイルを構築および管理する新しい方法が生まれました。それ以外の場合は、CSS-in-JSとして知られています。

スタイリングされたコンポーネントは、コンポーネントとスタイリングの間のギャップを埋めるCSS-in-JSツールであり、機能的で再利用可能な方法でスタイリングコンポーネントを起動して実行するための多数の機能を提供します。 この記事では、スタイル設定されたコンポーネントの基本と、それらをReactアプリケーションに適切に適用する方法を学習します。 このチュートリアルを実行する前に、以前にReactに取り組んでいたはずです。 Reactコンポーネントのスタイリングでさまざまなオプションを探している場合は、このテーマに関する以前の投稿を確認してください。

CSSの中核となるのは、DOMツリー内の位置に関係なく、任意のHTML要素をグローバルにターゲットにする機能です。 コンポーネントを使用する場合、これは障害になる可能性があります。これは、コンポーネントが、使用場所に近いコロケーション(つまり、状態やスタイルなどのアセットの保持)を合理的な範囲で要求するためです(ローカリゼーションと呼ばれます)。

React自身の言葉では、スタイル設定されたコンポーネントは「コンポーネントの視覚的なプリミティブ」であり、その目標は、コンポーネントをスタイル設定するための柔軟な方法を提供することです。 その結果、コンポーネントとそのスタイルが緊密に結合されます。

注:スタイル付きコンポーネントは、ReactとReact Nativeの両方で使用できます。必ず、React Nativeガイドを確認する必要がありますが、ここでは、Reactのスタイル付きコンポーネントに焦点を当てます。

ジャンプした後もっと! 以下を読み続けてください↓

なぜスタイル付きコンポーネントなのか?

スタイルのスコープ設定を支援するほかに、スタイル付きコンポーネントには次の機能が含まれています。

  • 自動ベンダープレフィックス
    標準のCSSプロパティを使用でき、スタイル付きコンポーネントは必要に応じてベンダープレフィックスを追加します。
  • 一意のクラス名
    スタイル付きコンポーネントは互いに独立しており、ライブラリが自動的に処理するため、名前について心配する必要はありません。
  • デッドスタイルの排除
    スタイル付きコンポーネントは、コードで宣言されている場合でも、未使用のスタイルを削除します。
  • などなど。

インストール

スタイル付きコンポーネントのインストールは簡単です。 CDNを介して、またはYarnなどのパッケージマネージャーを使用して実行できます…

 yarn add styled-components

…またはnpm:

 npm i styled-components

私たちのデモはcreate-react-appを使用しています。

はじめに

おそらく、スタイル付きコンポーネントについて最初に気付くのはその構文です。これは、スタイル付きコンポーネントの背後にある魔法を理解していない場合、気が遠くなる可能性があります。 簡単に言うと、スタイル付きコンポーネントはJavaScriptのテンプレートリテラルを使用して、コンポーネントとスタイルの間のギャップを埋めます。 したがって、スタイル付きコンポーネントを作成する場合、実際に作成しているのは、スタイルを備えたReactコンポーネントです。 次のようになります。

 import styled from "styled-components"; // Styled component named StyledButton const StyledButton = styled.button` background-color: black; font-size: 32px; color: white; `; function Component() { // Use it like any other component. return <StyledButton> Login </StyledButton>; }

ここで、 StyledButtonはスタイル付きコンポーネントであり、含まれているスタイルを含むHTMLボタンとしてレンダリングされます。 styledは、JavaScriptから実際のCSSにスタイリングを変換する内部ユーティリティメソッドです。

生のHTMLとCSSでは、次のようになります。

 button { background-color: black; font-size: 32px; color: white; } <button> Login </button>

スタイル付きコンポーネントがReactコンポーネントの場合、小道具を使用できますか? はい、できます。

小道具に基づく適応

スタイル設定されたコンポーネントは機能的であるため、要素を動的に簡単にスタイル設定できます。 ページに2種類のボタンがあり、1つは背景が黒で、もう1つは青であると仮定します。 それらのために2つのスタイル付きコンポーネントを作成する必要はありません。 私たちは彼らの小道具に基づいて彼らのスタイリングを適応させることができます。

 import styled from "styled-components"; const StyledButton = styled.button` min-width: 200px; border: none; font-size: 18px; padding: 7px 10px; /* The resulting background color will be based on the bg props. */ background-color: ${props => props.bg === "black" ? "black" : "blue"; `; function Profile() { return ( <div> <StyledButton bg="black">Button A</StyledButton> <StyledButton bg="blue">Button B</StyledButton> </div> ) }

StyledButtonは小道具を受け入れるReactコンポーネントであるため、 bg小道具の存在または値に基づいて異なる背景色を割り当てることができます。

ただし、ボタンにtypeが指定されていないことに気付くでしょう。 それをしましょう:

 function Profile() { return ( <> <StyledButton bg="black" type="button"> Button A </StyledButton> <StyledButton bg="blue" type="submit" onClick={() => alert("clicked")}> Button B </StyledButton> </> ); }

スタイル付きコンポーネントは、受け取る小道具のタイプを区別できます。 typeがHTML属性であることを知っているので、 bgプロップを独自の処理で使用しながら、実際に<button type="button">Button A</button>をレンダリングします。 イベントハンドラーをどのようにアタッチしたかにも注目してください。

属性について言えば、拡張構文を使用すると、 attrsコンストラクターを使用して小道具を管理できます。 これをチェックしてください:

 const StyledContainer = styled.section.attrs((props) => ({ width: props.width || "100%", hasPadding: props.hasPadding || false, }))` --container-padding: 20px; width: ${(props) => props.width}; // Falls back to 100% padding: ${(props) => (props.hasPadding && "var(--container-padding)") || "none"}; `;

幅を設定するときに3値が必要ないことに注意してください。 これは、 width: props.width || "100%", width: props.width || "100%", また、できるのでCSSカスタムプロパティを使用しました。

注:スタイル設定されたコンポーネントがReactコンポーネントであり、小道具を渡すことができる場合、状態も使用できますか? ライブラリのGitHubアカウントには、この問題に対処するための問題があります。

スタイルの拡張

ランディングページで作業していて、コンテナを特定の最大幅に設定して、中央に配置したとします。 そのためのStyledContainerがあります。

 const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `;

次に、20ピクセルではなく、両側に10ピクセルのパディングを備えた、より小さなコンテナが必要であることがわかります。 最初に考えたのは、別のスタイルのコンポーネントを作成することでした。その通りですが、スタイルを複製していることに気付くまで、それほど時間はかかりません。

 const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `; const StyledSmallContainer = styled.section` max-width: 1024px; padding: 0 10px; margin: 0 auto; `;

上記のスニペットのように、 StyledSmallContainerを作成する前に、スタイルを再利用して継承する方法を学びましょう。 これは、 spread演算子がどのように機能するかに多かれ少なかれ似ています。

 const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `; // Inherit StyledContainer in StyledSmallConatiner const StyledSmallContainer = styled(StyledContainer)` padding: 0 10px; `; function Home() { return ( <StyledContainer> <h1>The secret is to be happy</h1> </StyledContainer> ); } function Contact() { return ( <StyledSmallContainer> <h1>The road goes on and on</h1> </StyledSmallContainer> ); }

StyledSmallContainerでは、 StyledContainerからすべてのスタイルを取得しますが、パディングはオーバーライドされます。 通常、 StyledContainerがレンダリングするのは、 StyledSmallContainer用にレンダリングされたセクション要素であることに注意してください。 しかし、それはそれが石に刻まれている、または変更できないという意味ではありません。

「as」多型プロップ

as polymorphic propを使用すると、レンダリングされる終了要素を交換できます。 1つのユースケースは、スタイルを継承する場合です(最後の例のように)。 たとえば、 StyledSmallContainersectionよりもdivを使用したい場合は、次のように、好みの要素の値を使用しas aspropをスタイル付きコンポーネントに渡すことができます。

 function Home() { return ( <StyledContainer> <h1>It's business, not personal</h1> </StyledContainer> ); } function Contact() { return ( <StyledSmallContainer as="div"> <h1>Never dribble when you can pass</h1> </StyledSmallContainer> ); }

これで、 StyledSmallContainerdivとしてレンダリングされます。 値としてカスタムコンポーネントを使用することもできます。

 function Home() { return ( <StyledContainer> <h1>It's business, not personal</h1> </StyledContainer> ); } function Contact() { return ( <StyledSmallContainer as={StyledContainer}> <h1>Never dribble when you can pass</h1> </StyledSmallContainer> ); }

当たり前のことと思ってはいけません。

SCSSのような構文

CSSプリプロセッサStylisを使用すると、スタイル設定されたコンポーネントで、ネストなどのSCSSのような構文をサポートできます。

 const StyledProfileCard = styled.div` border: 1px solid black; > .username { font-size: 20px; color: black; transition: 0.2s; &:hover { color: red; } + .dob { color: grey; } } `; function ProfileCard() { return ( <StyledProfileCard> <h1 className="username">John Doe</h1> <p className="dob"> Date: <span>12th October, 2013</span> </p> <p className="gender">Male</p> </StyledProfileCard> ); }

アニメーション

スタイル付きコンポーネントには、(再利用可能な)アニメーションキーフレームの構築を支援するkeyframesヘルパーがあります。 ここでの利点は、キーフレームがスタイル設定されたコンポーネントから切り離され、必要な場所にエクスポートして再利用できることです。

 import styled, {keyframes} from "styled-components"; const slideIn = keyframes` from { opacity: 0; } to { opacity: 1; } `; const Toast = styled.div` animation: ${slideIn} 0.5s cubic-bezier(0.4, 0, 0.2, 1) both; border-radius: 5px; padding: 20px; position: fixed; `;

グローバルスタイリング

CSS-in-JSの当初の目標、ひいてはスタイル付きコンポーネントはスタイルのスコープですが、スタイル付きコンポーネントのグローバルなスタイルを活用することもできます。 私たちは主にスコープスタイルを扱っているので、それは不変の工場設定だと思うかもしれませんが、あなたは間違っているでしょう。 考えてみてください:実際にスコーピングとは何ですか? グローバルスタイリングの名の下に、私たちが次のようなことを行うことは技術的に可能です。

 ReactDOM.render( <StyledApp> <App /> </StyledApp>, document.getElementById("root") );

しかし、すでにヘルパー関数であるcreateGlobalStyleがあり、その唯一の存在理由はグローバルスタイルです。 それで、なぜそれがその責任を否定するのですか?

createGlobalStyleを使用できることの1つは、CSSを正規化することです。

 import {createGlobalStyle} from "styled-components"; const GlobalStyle = createGlobalStyle` /* Your css reset here */ `; // Use your GlobalStyle function App() { return ( <div> <GlobalStyle /> <Routes /> </div> ); }

注: createGlobalStyleで作成されたスタイルは、子を受け入れません。 詳細については、ドキュメントをご覧ください。

この時点で、なぜcreateGlobalStlyeを使用する必要があるのか​​疑問に思われるかもしれません。 ここにいくつかの理由があります:

  • ルートレンダリングの外側にあるもの(たとえば、 htmlbodyなど)をターゲットにすることはできません。
  • createGlobalStyleはスタイルを挿入しますが、実際の要素はレンダリングしません。 最後の例をよく見ると、レンダリングするHTML要素が指定されていないことがわかります。 実際には要素が必要ないかもしれないので、これはクールです。 結局のところ、私たちはグローバルスタイルに関心を持っています。 特定の要素ではなく、セレクター全体を対象としています。
  • createGlobalStyleはスコープがなく、アプリのどこにでもレンダリングでき、DOMにある限り適用できます。 構造ではなく、概念について考えてください。
 import {createGlobalStyle} from "styled-components"; const GlobalStyle = createGlobalStyle` /* Your css reset here */ .app-title { font-size: 40px; } `; const StyledNav = styled.nav` /* Your styles here */ `; function Nav({children}) { return ( <StyledNav> <GlobalStyle /> {children} </StyledNav> ); } function App() { return ( <div> <Nav> <h1 className="app-title">STYLED COMPONENTS</h1> </Nav> <Main /> <Footer /> </div> ); }

構造について考える場合、 app-titleGlobalStyleで設定されたスタイルに設定しないでください。 しかし、それはそのようには機能しません。 GlobalStyleをレンダリングすることを選択した場合は常に、コンポーネントがレンダリングされるときに注入されます。

注意createGlobalStylesは、DOMにある場合にのみレンダリングされます。

CSSヘルパー

小道具に基づいてスタイルを適応させる方法はすでに見てきました。 もう少し先に進みたい場合はどうなりますか? CSSヘルパー関数はこれを実現するのに役立ちます。 空とアクティブの2つのテキスト入力フィールドがあり、それぞれが異なる色であると仮定します。 できるよ:

 const StyledTextField = styled.input` color: ${(props) => (props.isEmpty ? "none" : "black")}; `;

すべて順調です。 その後、別の塗りつぶし状態を追加する必要がある場合は、スタイルを変更する必要があります。

 const StyledTextField = styled.input` color: ${(props) => props.isEmpty ? "none" : props.active ? "purple" : "blue"}; `;

現在、三項演算は複雑さを増しています。 後でテキスト入力フィールドに別の状態を追加するとどうなりますか? または、各州に色以外の追加のスタイルを与えたい場合はどうなりますか? 三項演算にスタイルを詰め込むことを想像できますか? cssヘルパーが便利です。

 const StyledTextField = styled.input` width: 100%; height: 40px; ${(props) => (props.empty && css` color: none; backgroundcolor: white; `) || (props.active && css` color: black; backgroundcolor: whitesmoke; `)} `;

私たちが行ったことは、より多くのスタイルに対応するために、より理解しやすく整理された構文を使用して、三項構文を拡張したものです。 前のステートメントが間違っているように思われる場合は、コードが多くのことを実行しようとしていることが原因です。 それでは、一歩下がって洗練してみましょう。

 const StyledTextField = styled.input` width: 100%; height: 40px; // 1. Empty state ${(props) => props.empty && css` color: none; backgroundcolor: white; `} // 2. Active state ${(props) => props.active && css` color: black; backgroundcolor: whitesmoke; `} // 3. Filled state ${(props) => props.filled && css` color: black; backgroundcolor: white; border: 1px solid green; `} `;

私たちの改良により、スタイリングは3つの異なる管理しやすく理解しやすいチャンクに分割されます。 それは勝利です。

StyleSheetManager

CSSヘルパーと同様に、 StyleSheetManagerは、スタイルの処理方法を変更するためのヘルパーメソッドです。 サブツリーからベンダープレフィックスをオプトアウトするのに役立つ、 disableVendorPrefixes (完全なリストを確認できます)などの特定の小道具が必要です。

 import styled, {StyleSheetManager} from "styled-components"; const StyledCard = styled.div` width: 200px; backgroundcolor: white; `; const StyledNav = styled.div` width: calc(100% - var(--side-nav-width)); `; function Profile() { return ( <div> <StyledNav /> <StyleSheetManager disableVendorPrefixes> <StyledCard> This is a card </StyledCard> </StyleSheetManager> </div> ); }

disableVendorPrefixesは、小道具として<StyleSheetManager>に渡されます。 したがって、 <StyleSheetManager>でラップされたスタイル付きコンポーネントは無効になりますが、 <StyledNav>のコンポーネントは無効になりません。

より簡単なデバッグ

スタイリングされたコンポーネントを同僚の1人に紹介したとき、彼らの不満の1つは、レンダリングされた要素をDOMまたはReact DeveloperToolsで見つけるのが難しいということでした。 これは、スタイル付きコンポーネントの欠点の1つです。一意のクラス名を提供しようとすると、要素に一意のハッシュが割り当てられます。これは、たまたま不可解ですが、 displayNameを読み取り可能にして、デバッグを容易にします。

 import React from "react"; import styled from "styled-components"; import "./App.css"; const LoginButton = styled.button` background-color: white; color: black; border: 1px solid red; `; function App() { return ( <div className="App"> <LoginButton>Login</LoginButton> </div> ); }

デフォルトでは、スタイル付きコンポーネントは、 LoginButtonをDOMでは<button class="LoginButton-xxxx xxxx">Login</button>としてレンダリングし、React DeveloperToolsではLoginButtonとしてレンダリングします。これによりデバッグが容易になります。 この動作が必要ない場合は、 displayNameブール値を切り替えることができます。 これには、Babel構成が必要です。

:ドキュメントでは、パッケージbabel-plugin-styled-components.babelrc構成ファイルが指定されています。 これに関する問題は、 create-react-appを使用しているため、イジェクトしない限り多くの設定を行うことができないということです。 これがバベルマクロの出番です。

npmまたはYarnを使用してbabel-plugin-macrosをインストールしてから、アプリケーションのルートに次のコンテンツを含むbabel-plugin-macros.config.jsを作成する必要があります。

 module.exports = { styledComponents: { displayName: true, fileName: false, }, };

fileNameの値を逆にすると、 displayNameの前にファイル名が付けられ、さらに一意の精度が得られます。

また、 macroからインポートする必要があります。

 // Before import styled from "styled-components"; // After import styled from "styled-components/macro";

結論

プログラムでCSSを作成できるようになったので、自由を乱用しないでください。 その価値については、スタイル設定されたコンポーネントの健全性を維持するために最善を尽くしてください。 重い条件文を作成しようとしないでください。また、すべてのものをスタイル付きコンポーネントにする必要があると思い込まないでください。 また、角を曲がったところにあると推測しているだけのユースケース用に、初期のスタイルのコンポーネントを作成して過度に抽象化しないでください。

その他のリソース

  1. ドキュメント、スタイル付きコンポーネント
  2. 「React.jsとstyled-componentsを使用した再利用可能なコンポーネントシステムの構築」、Lukas Gisder-Dube
  3. Next.jsでの使用
  4. Gatsbyでの使用