So verwenden Sie gestylte Komponenten in React
Veröffentlicht: 2022-03-10Styled-Komponenten sind ein CSS-in-JS-Tool, das die Lücke zwischen Komponenten und Styling schließt und zahlreiche Features bietet, die Ihnen helfen, Styling-Komponenten auf funktionale und wiederverwendbare Weise zum Laufen zu bringen. In diesem Artikel lernen Sie die Grundlagen von gestylten Komponenten und wie Sie sie richtig auf Ihre React-Anwendungen anwenden. Sie sollten zuvor an React gearbeitet haben, bevor Sie dieses Tutorial durcharbeiten. Wenn Sie nach verschiedenen Optionen für das Styling von React-Komponenten suchen, können Sie sich unseren vorherigen Beitrag zu diesem Thema ansehen.
Der Kern von CSS ist die Fähigkeit, jedes HTML-Element – global – anzusprechen, unabhängig von seiner Position im DOM-Baum. Dies kann ein Hindernis sein, wenn es mit Komponenten verwendet wird, da Komponenten in einem angemessenen Umfang Colocation erfordern (dh Assets wie Status und Styling) näher an der Stelle halten, an der sie verwendet werden (bekannt als Lokalisierung).
In Reacts eigenen Worten sind gestylte Komponenten „ visuelle Primitive für Komponenten “, und ihr Ziel ist es, uns eine flexible Möglichkeit zu geben, Komponenten zu stylen. Das Ergebnis ist eine enge Kopplung zwischen Komponenten und ihren Stilen.
Hinweis: Gestylte Komponenten sind sowohl für React als auch für React Native verfügbar, und obwohl Sie unbedingt den React Native-Leitfaden lesen sollten, liegt unser Fokus hier auf gestylten Komponenten für React.
Warum gestaltete Komponenten?
Abgesehen davon, dass sie Ihnen helfen, Stile zu definieren, beinhalten Stilkomponenten die folgenden Funktionen:
- Automatische Anbieterpräfixierung
Sie können Standard-CSS-Eigenschaften verwenden, und formatierte Komponenten fügen bei Bedarf Herstellerpräfixe hinzu. - Eindeutige Klassennamen
Gestylte Komponenten sind voneinander unabhängig, und Sie müssen sich keine Gedanken über ihre Namen machen, da die Bibliothek dies für Sie übernimmt. - Eliminierung toter Stile
Mit Stil versehene Komponenten entfernen ungenutzte Stile, auch wenn sie in Ihrem Code deklariert sind. - und viele mehr.
Installation
Die Installation von gestylten Komponenten ist einfach. Sie können dies über ein CDN oder mit einem Paketmanager wie Yarn tun …
yarn add styled-components
… oder npm:
npm i styled-components
Unsere Demo verwendet create-react-app.
Ausgehen
Das Erste, was Ihnen bei gestylten Komponenten auffallen wird, ist vielleicht ihre Syntax, die entmutigend sein kann, wenn Sie die Magie hinter gestylten Komponenten nicht verstehen. Kurz gesagt, gestylte Komponenten verwenden die Template-Literale von JavaScript, um die Lücke zwischen Komponenten und Stilen zu schließen. Wenn Sie also eine gestylte Komponente erstellen, erstellen Sie eigentlich eine React-Komponente mit Stilen. Es sieht aus wie das:
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>; }
Hier ist StyledButton
die gestaltete Komponente und wird als HTML-Schaltfläche mit den enthaltenen Stilen gerendert. styled
ist eine interne Hilfsmethode, die das Styling von JavaScript in tatsächliches CSS umwandelt.
In rohem HTML und CSS hätten wir Folgendes:
button { background-color: black; font-size: 32px; color: white; } <button> Login </button>
Wenn gestylte Komponenten React-Komponenten sind, können wir Requisiten verwenden? Ja wir können.
Anpassung basierend auf Requisiten
Gestylte Komponenten sind funktional , daher können wir Elemente einfach dynamisch gestalten. Nehmen wir an, wir haben zwei Arten von Schaltflächen auf unserer Seite, eine mit schwarzem Hintergrund und die andere mit blauem Hintergrund. Wir müssen nicht zwei gestylte Komponenten für sie erstellen; Wir können ihr Styling basierend auf ihren Requisiten anpassen.
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> ) }
Da StyledButton
eine React-Komponente ist, die Requisiten akzeptiert, können wir basierend auf der Existenz oder dem Wert des bg
-Props eine andere Hintergrundfarbe zuweisen.
Sie werden jedoch feststellen, dass wir unserem Button keinen type
gegeben haben. Lass uns das tun:
function Profile() { return ( <> <StyledButton bg="black" type="button"> Button A </StyledButton> <StyledButton bg="blue" type="submit" onClick={() => alert("clicked")}> Button B </StyledButton> </> ); }
Gestylte Komponenten können zwischen den Arten von Requisiten unterscheiden, die sie erhalten. Sie wissen, dass type
ein HTML-Attribut ist, also rendern sie tatsächlich <button type="button">Button A</button>
, während sie die bg
-Prop in ihrer eigenen Verarbeitung verwenden. Beachten Sie, wie wir auch einen Event-Handler angehängt haben?
Apropos Attribute: Eine erweiterte Syntax ermöglicht es uns, Requisiten mit dem attrs
Konstruktor zu verwalten. Sieh dir das an:
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"}; `;
Beachten Sie, dass wir beim Festlegen der Breite keinen Ternär benötigen? Das liegt daran, dass wir mit width: props.width || "100%",
width: props.width || "100%",
. Außerdem haben wir benutzerdefinierte CSS-Eigenschaften verwendet, weil wir es können!
Hinweis: Wenn gestylte Komponenten React-Komponenten sind und wir Requisiten übergeben können, können wir dann auch Zustände verwenden? Das GitHub-Konto der Bibliothek hat ein Problem, das sich genau mit dieser Angelegenheit befasst.
Stile erweitern
Nehmen wir an, Sie arbeiten an einer Zielseite und haben Ihren Container auf eine bestimmte maximale Breite eingestellt, um die Dinge zentriert zu halten. Dafür haben Sie einen StyledContainer
:
const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `;
Dann stellen Sie fest, dass Sie einen kleineren Container mit einer Auffüllung von 10 Pixeln auf beiden Seiten anstelle von 20 Pixeln benötigen. Ihr erster Gedanke wäre vielleicht, eine weitere Stilkomponente zu erstellen, und Sie hätten Recht, aber es würde nicht lange dauern, bis Sie erkennen, dass Sie Stile duplizieren.
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; `;
Bevor Sie fortfahren und StyledSmallContainer
erstellen, wie im obigen Codeausschnitt, lernen wir, wie Sie Stile wiederverwenden und erben. Es ist mehr oder weniger so, wie der spread
-Operator funktioniert:
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> ); }
In Ihrem StyledSmallContainer
erhalten Sie alle Stile von StyledContainer
, aber die Auffüllung wird überschrieben. Denken Sie daran, dass normalerweise ein Abschnittselement für StyledSmallContainer
gerendert wird, da StyledContainer
das rendert. Aber das bedeutet nicht, dass es in Stein gemeißelt oder unveränderlich ist.
Die „als“ polymorphe Requisite
Mit der as
polymorphe Requisite können Sie das Endelement austauschen, das gerendert wird. Ein Anwendungsfall ist das Vererben von Stilen (wie im letzten Beispiel). Wenn Sie beispielsweise ein div
einem section
für StyledSmallContainer
, können Sie das as
Prop an Ihre formatierte Komponente mit dem Wert Ihres bevorzugten Elements übergeben, etwa so:
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> ); }
Jetzt wird StyledSmallContainer
als div
gerendert. Sie könnten sogar eine benutzerdefinierte Komponente als Wert haben:
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> ); }
Sieh es nicht als selbstverständlich an.
SCSS-ähnliche Syntax
Der CSS-Präprozessor Stylis ermöglicht formatierten Komponenten die Unterstützung von SCSS-ähnlicher Syntax, wie z. B. Verschachtelung:
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> ); }
Animation
Stilisierte Komponenten verfügen über einen keyframes
-Helfer, der beim Erstellen von (wiederverwendbaren) Animations-Keyframes hilft. Der Vorteil hierbei ist, dass die Keyframes von den gestylten Komponenten getrennt werden und exportiert und bei Bedarf wiederverwendet werden können.
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; `;
Globales Styling
Während das ursprüngliche Ziel von CSS-in-JS und damit auch von gestylten Komponenten das Scoping von Stilen ist, können wir auch das globale Styling von gestylten Komponenten nutzen. Da wir meistens mit Scoped Styles arbeiten, könnte man meinen, dass dies eine unveränderliche Werkseinstellung ist, aber Sie liegen falsch. Denken Sie darüber nach: Was ist eigentlich Scoping? Es ist uns technisch möglich – im Namen des globalen Stylings – etwas Ähnliches zu tun:
ReactDOM.render( <StyledApp> <App /> </StyledApp>, document.getElementById("root") );
Aber wir haben bereits eine Hilfsfunktion – createGlobalStyle
– deren einzige Existenzberechtigung das globale Styling ist. Warum ihm also seine Verantwortung verweigern?
Eine Sache, für die wir createGlobalStyle
verwenden können, ist das CSS zu normalisieren:
import {createGlobalStyle} from "styled-components"; const GlobalStyle = createGlobalStyle` /* Your css reset here */ `; // Use your GlobalStyle function App() { return ( <div> <GlobalStyle /> <Routes /> </div> ); }
Hinweis: Stile, die mit createGlobalStyle
erstellt wurden, akzeptieren keine untergeordneten Elemente. Erfahren Sie mehr in der Dokumentation.
An dieser Stelle fragen Sie sich vielleicht, warum wir uns überhaupt die Mühe machen sollten, createGlobalStlye
zu verwenden. Hier sind einige Gründe:
- Wir können nichts außerhalb des Root-Renderings ohne es ansprechen (z. B.
html
,body
usw.). -
createGlobalStyle
Stile ein, rendert jedoch keine tatsächlichen Elemente. Wenn Sie sich das letzte Beispiel genau ansehen, werden Sie feststellen, dass wir kein zu renderndes HTML-Element angegeben haben. Das ist cool, weil wir das Element vielleicht gar nicht brauchen. Schließlich geht es uns um globale Styles. Wir zielen auf Selektoren im Allgemeinen ab, nicht auf bestimmte Elemente. -
createGlobalStyle
hat keinen Bereich und kann überall in unserer App gerendert werden und ist anwendbar, solange es sich im DOM befindet. Denken Sie an das Konzept , nicht an die Struktur .
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> ); }
Wenn Sie über die Struktur nachdenken, sollte der app-title
nicht wie in GlobalStyle
festgelegt formatiert werden. Aber so geht das nicht. Wo auch immer Sie Ihren GlobalStyle
rendern, er wird eingefügt, wenn Ihre Komponente gerendert wird.
Seien Sie vorsichtig : createGlobalStyles
wird nur gerendert, wenn es sich im DOM befindet.
CSS-Helfer
Wir haben bereits gesehen, wie man Stile basierend auf Requisiten anpasst. Was wäre, wenn wir noch ein bisschen weiter gehen wollten? Dabei hilft die CSS-Hilfsfunktion. Nehmen wir an, wir haben zwei Texteingabefelder mit Zuständen: leer und aktiv, jedes mit einer anderen Farbe. Wir können das schaffen:
const StyledTextField = styled.input` color: ${(props) => (props.isEmpty ? "none" : "black")}; `;
Alles ist gut. Wenn wir anschließend einen weiteren Füllzustand hinzufügen müssen, müssen wir unsere Stile ändern:
const StyledTextField = styled.input` color: ${(props) => props.isEmpty ? "none" : props.active ? "purple" : "blue"}; `;
Jetzt wird die ternäre Operation immer komplexer. Was ist, wenn wir unseren Texteingabefeldern später einen weiteren Status hinzufügen? Oder was ist, wenn wir jedem Staat außer Farbe zusätzliche Stile geben wollen? Können Sie sich vorstellen, die Stile in die ternäre Operation einzuzwängen? Der css
Helfer ist praktisch.
const StyledTextField = styled.input` width: 100%; height: 40px; ${(props) => (props.empty && css` color: none; backgroundcolor: white; `) || (props.active && css` color: black; backgroundcolor: whitesmoke; `)} `;
Was wir getan haben, ist, unsere ternäre Syntax zu erweitern, um mehr Stile aufzunehmen, und mit einer verständlicheren und organisierteren Syntax. Wenn die vorherige Aussage falsch erscheint, liegt das daran, dass der Code versucht, zu viel zu tun. Gehen wir also einen Schritt zurück und verfeinern:
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; `} `;
Unsere Verfeinerung teilt das Styling in drei verschiedene überschaubare und leicht verständliche Teile auf. Es ist ein Gewinn.
StyleSheetManager
Wie der CSS-Helfer ist StyleSheetManager
eine Hilfsmethode zum Ändern der Verarbeitung von Stilen. Es braucht bestimmte Requisiten – wie disableVendorPrefixes
(Sie können sich die vollständige Liste ansehen) – die Ihnen helfen, Anbieterpräfixe aus seinem Unterbaum abzulehnen.
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
wird als Prop an <StyleSheetManager>
. Die von <StyleSheetManager>
Komponenten würden also deaktiviert, aber nicht die in <StyledNav>
.
Einfacheres Debuggen
Als ich einem meiner Kollegen gestylte Komponenten vorstellte, war eine seiner Beschwerden, dass es schwierig ist, ein gerendertes Element im DOM – oder in der Tat in den React Developer Tools – zu finden. Dies ist einer der Nachteile von Stilkomponenten: Beim Versuch, eindeutige Klassennamen bereitzustellen, weist sie Elementen eindeutige Hashes zu, die zufälligerweise kryptisch sind, aber sie machen den displayName
lesbar, um das Debuggen zu erleichtern.
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> ); }
Standardmäßig rendern formatierte Komponenten LoginButton
als <button class="LoginButton-xxxx xxxx">Login</button>
im DOM und als LoginButton
in React Developer Tools, was das Debuggen erleichtert. Wir können den booleschen displayName
, wenn wir dieses Verhalten nicht wollen. Dies erfordert eine Babel-Konfiguration.
Hinweis : In der Dokumentation wird das Paket babel-plugin-styled-components
sowie eine .babelrc
Konfigurationsdatei angegeben. Das Problem dabei ist, dass wir, weil wir create-react-app
verwenden, viele Dinge nicht konfigurieren können, es sei denn, wir werfen aus. Hier kommen Babel-Makros ins Spiel.
Wir müssen babel-plugin-macros
mit npm oder Yarn installieren und dann eine babel-plugin-macros.config.js
im Stammverzeichnis unserer Anwendung mit folgendem Inhalt erstellen:
module.exports = { styledComponents: { displayName: true, fileName: false, }, };
Wenn der fileName
Wert invertiert ist, wird dem displayName
der Dateiname vorangestellt, um eine noch eindeutigere Genauigkeit zu erzielen.
Wir müssen jetzt auch aus dem macro
importieren:
// Before import styled from "styled-components"; // After import styled from "styled-components/macro";
Fazit
Jetzt, da Sie Ihr CSS programmgesteuert zusammenstellen können, sollten Sie diese Freiheit nicht missbrauchen. Für das, was es wert ist, geben Sie Ihr Bestes, um die Gesundheit Ihrer gestylten Komponenten zu erhalten. Versuchen Sie nicht, schwere Bedingungen zu formulieren, und nehmen Sie auch nicht an, dass jedes Ding eine gestylte Komponente sein sollte. Außerdem sollten Sie nicht zu stark abstrahieren, indem Sie neu gestaltete Komponenten für Anwendungsfälle erstellen, von denen Sie nur vermuten, dass sie irgendwo um die Ecke liegen.
Weitere Ressourcen
- Dokumentation, Gestylte Komponenten
- „Aufbau eines wiederverwendbaren Komponentensystems mit React.js und styled-components“, Lukas Gisder-Dube
- Verwendung mit Next.js
- Verwendung mit Gatsby