Comment utiliser les composants stylés dans React
Publié: 2022-03-10Les composants stylisés sont un outil CSS-in-JS qui comble le fossé entre les composants et le style, offrant de nombreuses fonctionnalités pour vous permettre d'être opérationnel dans les composants de style de manière fonctionnelle et réutilisable. Dans cet article, vous apprendrez les bases des composants stylisés et comment les appliquer correctement à vos applications React. Vous devriez avoir travaillé sur React auparavant avant de suivre ce tutoriel. Si vous recherchez diverses options dans le style des composants React, vous pouvez consulter notre article précédent sur le sujet.
Au cœur de CSS se trouve la capacité de cibler n'importe quel élément HTML - globalement - quelle que soit sa position dans l'arborescence DOM. Cela peut être un obstacle lorsqu'il est utilisé avec des composants, car les composants exigent, dans une mesure raisonnable, une colocation (c'est-à-dire en gardant les actifs tels que les états et le style) plus près de l'endroit où ils sont utilisés (appelée localisation).
Selon les propres termes de React, les composants stylisés sont des « primitives visuelles pour les composants », et leur objectif est de nous donner un moyen flexible de styliser les composants. Le résultat est un couplage étroit entre les composants et leurs styles.
Remarque : les composants stylés sont disponibles à la fois pour React et React Native, et bien que vous deviez absolument consulter le guide React Native, nous nous concentrerons ici sur les composants stylés pour React.
Pourquoi des composants stylés ?
En plus de vous aider à déterminer la portée des styles, les composants stylés incluent les fonctionnalités suivantes :
- Préfixe automatique du fournisseur
Vous pouvez utiliser des propriétés CSS standard et les composants stylisés ajouteront des préfixes de fournisseur si nécessaire. - Noms de classe uniques
Les composants stylés sont indépendants les uns des autres et vous n'avez pas à vous soucier de leurs noms car la bibliothèque s'en charge pour vous. - Élimination des styles morts
Les composants stylés suppriment les styles inutilisés, même s'ils sont déclarés dans votre code. - et beaucoup plus.
Installation
L'installation de composants stylés est facile. Vous pouvez le faire via un CDN ou avec un gestionnaire de packages tel que Yarn…
yarn add styled-components
… ou npm :
npm i styled-components
Notre démo utilise create-react-app.
Commençant
Peut-être que la première chose que vous remarquerez à propos des composants stylés est leur syntaxe, qui peut être intimidante si vous ne comprenez pas la magie derrière les composants stylés. Pour le dire brièvement, les composants stylés utilisent les littéraux de modèle de JavaScript pour combler le fossé entre les composants et les styles. Ainsi, lorsque vous créez un composant stylé, vous créez en fait un composant React avec des styles. Il ressemble à ceci :
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>; }
Ici, StyledButton
est le composant stylé, et il sera rendu sous la forme d'un bouton HTML avec les styles contenus. styled
est une méthode utilitaire interne qui transforme le style de JavaScript en véritable CSS.
En HTML brut et CSS, nous aurions ceci :
button { background-color: black; font-size: 32px; color: white; } <button> Login </button>
Si les composants stylés sont des composants React, pouvons-nous utiliser des accessoires ? Oui nous pouvons.
Adaptation basée sur les accessoires
Les composants stylisés sont fonctionnels , nous pouvons donc facilement styliser les éléments de manière dynamique. Supposons que nous ayons deux types de boutons sur notre page, l'un avec un fond noir et l'autre bleu. Nous n'avons pas besoin de créer deux composants stylés pour eux ; nous pouvons adapter leur style en fonction de leurs accessoires.
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> ) }
Étant donné que StyledButton
est un composant React qui accepte les accessoires, nous pouvons attribuer une couleur d'arrière-plan différente en fonction de l'existence ou de la valeur de l'accessoire bg
.
Vous remarquerez cependant que nous n'avons pas donné de type
à notre bouton . Faisons cela:
function Profile() { return ( <> <StyledButton bg="black" type="button"> Button A </StyledButton> <StyledButton bg="blue" type="submit" onClick={() => alert("clicked")}> Button B </StyledButton> </> ); }
Les composants stylisés peuvent différencier les types d'accessoires qu'ils reçoivent. Ils savent que type
est un attribut HTML, donc ils rendent en fait <button type="button">Button A</button>
, tout en utilisant la prop bg
dans leur propre traitement. Remarquez comment nous avons également attaché un gestionnaire d'événements ?
En parlant d'attributs, une syntaxe étendue nous permet de gérer les accessoires à l'aide du constructeur attrs
. Regarde ça:
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"}; `;
Remarquez que nous n'avons pas besoin d'un ternaire lors du réglage de la largeur ? C'est parce que nous lui avons déjà défini une width: props.width || "100%",
width: props.width || "100%",
. De plus, nous avons utilisé les propriétés personnalisées CSS parce que nous le pouvons !
Remarque : Si les composants stylés sont des composants React et que nous pouvons transmettre des accessoires, pouvons-nous également utiliser des états ? Le compte GitHub de la bibliothèque a un problème concernant ce problème.
Étendre les styles
Disons que vous travaillez sur une page de destination et que vous avez défini votre conteneur sur une certaine largeur maximale pour que les choses restent centrées. Vous avez un StyledContainer
pour cela :
const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `;
Ensuite, vous découvrez que vous avez besoin d'un conteneur plus petit, avec un rembourrage de 10 pixels des deux côtés, au lieu de 20 pixels. Votre première pensée pourrait être de créer un autre composant stylé, et vous auriez raison, mais cela ne prendrait pas de temps avant que vous réalisiez que vous dupliquez des styles.
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; `;
Avant de continuer et de créer StyledSmallContainer
, comme dans l'extrait ci-dessus, apprenons comment réutiliser et hériter des styles. C'est plus ou moins comme le fonctionnement de l'opérateur de 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> ); }
Dans votre StyledSmallContainer
, vous obtiendrez tous les styles de StyledContainer
, mais le rembourrage sera remplacé. Gardez à l'esprit que, normalement, vous obtiendrez un élément de section rendu pour StyledSmallContainer
, car c'est ce que StyledContainer
rend. Mais cela ne veut pas dire qu'il est gravé dans la pierre ou immuable.
L'accessoire polymorphe "comme"
Avec le prop polymorphe as
, vous pouvez échanger l'élément final qui est rendu. Un cas d'utilisation est lorsque vous héritez de styles (comme dans le dernier exemple). Si, par exemple, vous préférez un div
à une section
pour StyledSmallContainer
, vous pouvez passer as
prop à votre composant stylé avec la valeur de votre élément préféré, comme ceci :
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> ); }
Maintenant, StyledSmallContainer
sera rendu sous la forme d'un div
. Vous pourriez même avoir un composant personnalisé comme valeur :
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> ); }
Ne le prenez pas pour acquis.
Syntaxe de type SCSS
Le préprocesseur CSS Stylis permet aux composants stylisés de prendre en charge une syntaxe de type SCSS, telle que l'imbrication :
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
Les composants stylisés ont un assistant d' keyframes
qui aide à construire des images clés d'animation (réutilisables). L'avantage ici est que les images clés seront détachées des composants stylés et pourront être exportées et réutilisées partout où cela sera nécessaire.
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; `;
Style global
Bien que l'objectif initial de CSS-in-JS et, par extension, des composants stylés soit la portée des styles, nous pouvons également tirer parti du style global des composants stylés. Étant donné que nous travaillons principalement avec des styles étendus, vous pourriez penser qu'il s'agit d'un paramètre d'usine invariable, mais vous vous trompez. Pensez-y : qu'est-ce que le cadrage ? Il nous est techniquement possible, au nom du style global, de faire quelque chose de similaire :
ReactDOM.render( <StyledApp> <App /> </StyledApp>, document.getElementById("root") );
Mais nous avons déjà une fonction d'assistance - createGlobalStyle
- dont la seule raison d'être est le style global. Alors, pourquoi lui dénier sa responsabilité ?
Une chose pour laquelle nous pouvons utiliser createGlobalStyle
est de normaliser le CSS :
import {createGlobalStyle} from "styled-components"; const GlobalStyle = createGlobalStyle` /* Your css reset here */ `; // Use your GlobalStyle function App() { return ( <div> <GlobalStyle /> <Routes /> </div> ); }
Remarque : Les styles créés avec createGlobalStyle
n'acceptent aucun enfant. En savoir plus dans la documentation.
À ce stade, vous vous demandez peut-être pourquoi nous devrions nous embêter à utiliser createGlobalStlye
. Voici quelques raisons:
- Nous ne pouvons rien cibler en dehors du rendu racine sans lui (par exemple,
html
,body
, etc.). -
createGlobalStyle
injecte des styles mais ne restitue aucun élément réel. Si vous regardez attentivement le dernier exemple, vous remarquerez que nous n'avons spécifié aucun élément HTML à afficher. C'est cool parce que nous n'avons peut-être pas réellement besoin de l'élément. Après tout, nous sommes concernés par les styles globaux. Nous ciblons les sélecteurs au sens large, pas des éléments spécifiques. -
createGlobalStyle
n'est pas délimité et peut être rendu n'importe où dans notre application et sera applicable tant qu'il se trouve dans le DOM. Pensez au concept , pas à la structure .
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> ); }
Si vous pensez à la structure, app-title
ne doit pas être stylisé comme défini dans GlobalStyle
. Mais ça ne marche pas comme ça. Où que vous choisissiez de rendre votre GlobalStyle
, il sera injecté lors du rendu de votre composant .
Attention : createGlobalStyles
ne sera rendu que si et quand il est dans le DOM.
Aide CSS
Nous avons déjà vu comment adapter les styles en fonction des accessoires. Et si on voulait aller un peu plus loin ? La fonction d'assistance CSS aide à y parvenir. Supposons que nous ayons deux champs de saisie de texte avec des états : vide et actif, chacun avec une couleur différente. Nous pouvons le faire:
const StyledTextField = styled.input` color: ${(props) => (props.isEmpty ? "none" : "black")}; `;
Tout va bien. Par la suite, si nous devons ajouter un autre état de remplissage, nous devrons modifier nos styles :
const StyledTextField = styled.input` color: ${(props) => props.isEmpty ? "none" : props.active ? "purple" : "blue"}; `;
Or l'opération ternaire gagne en complexité. Et si nous ajoutions un autre état à nos champs de saisie de texte plus tard ? Ou que se passe-t-il si nous voulons donner à chaque état des styles supplémentaires, autres que la couleur ? Pouvez-vous imaginer enfermer les styles dans l'opération ternaire ? L'assistant css
est très pratique.
const StyledTextField = styled.input` width: 100%; height: 40px; ${(props) => (props.empty && css` color: none; backgroundcolor: white; `) || (props.active && css` color: black; backgroundcolor: whitesmoke; `)} `;
Ce que nous avons fait, c'est en quelque sorte étendre notre syntaxe ternaire pour accueillir plus de styles, et avec une syntaxe plus compréhensible et organisée. Si la déclaration précédente semble fausse, c'est parce que le code essaie d'en faire trop. Alors, revenons en arrière et affinons:
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; `} `;
Notre raffinement divise le style en trois morceaux gérables et faciles à comprendre. C'est une victoire.
Gestionnaire de feuilles de style
Comme l'assistant CSS, StyleSheetManager
est une méthode d'assistance pour modifier la façon dont les styles sont traités. Il faut certains accessoires - comme disableVendorPrefixes
(vous pouvez consulter la liste complète) - qui vous aident à désactiver les préfixes de fournisseur de sa sous-arborescence.
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
est transmis en tant que prop à <StyleSheetManager>
. Ainsi, les composants stylés enveloppés par <StyleSheetManager>
seraient désactivés, mais pas ceux de <StyledNav>
.
Débogage plus facile
Lors de la présentation de composants stylés à l'un de mes collègues, l'une de leurs plaintes était qu'il était difficile de localiser un élément rendu dans le DOM - ou dans React Developer Tools, d'ailleurs. C'est l'un des inconvénients des composants stylés : en essayant de fournir des noms de classe uniques, il attribue des hachages uniques aux éléments, qui se trouvent être cryptés, mais cela rend le displayName
lisible pour un débogage plus facile.
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> ); }
Par défaut, les composants stylés rendent LoginButton
en tant que <button class="LoginButton-xxxx xxxx">Login</button>
dans le DOM, et en tant que LoginButton
dans React Developer Tools, ce qui facilite le débogage. Nous pouvons basculer le booléen displayName
si nous ne voulons pas ce comportement. Cela nécessite une configuration Babel.
Note : Dans la documentation, le package babel-plugin-styled-components
est spécifié, ainsi qu'un fichier de configuration .babelrc
. Le problème avec ceci est que, parce que nous utilisons create-react-app
, nous ne pouvons pas configurer beaucoup de choses à moins d'éjecter. C'est là qu'interviennent les macros Babel.
Nous devrons installer babel-plugin-macros
avec npm ou Yarn, puis créer un babel-plugin-macros.config.js
à la racine de notre application, avec le contenu :
module.exports = { styledComponents: { displayName: true, fileName: false, }, };
Avec la valeur fileName
inversée, le displayName
sera préfixé avec le nom du fichier pour une précision encore plus unique.
Nous devons également maintenant importer depuis la macro
:
// Before import styled from "styled-components"; // After import styled from "styled-components/macro";
Conclusion
Maintenant que vous pouvez composer votre CSS par programmation, n'abusez pas de la liberté. Pour ce que ça vaut, faites de votre mieux pour maintenir la raison dans vos composants stylés. N'essayez pas de composer des conditionnels lourds, ni de supposer que tout devrait être un composant stylé. De plus, ne faites pas trop d'abstraction en créant des composants de style naissant pour des cas d'utilisation dont vous ne faites que deviner qu'ils sont quelque part au coin de la rue.
Autres ressources
- Documentation, composants stylés
- "Construire un système de composants réutilisables avec React.js et des composants stylés", Lukas Gisder-Dube
- Utilisation avec Next.js
- Utilisation avec Gatsby