En utilisant children
const Wrapper = ({children}) => (
<div>
<div>header</div>
<div>{children}</div>
<div>footer</div>
</div>
);
const App = ({name}) => <div>Hello {name}</div>;
const WrappedApp = ({name}) => (
<Wrapper>
<App name={name}/>
</Wrapper>
);
render(<WrappedApp name="toto"/>,node);
Ceci est également connu sous le nom transclusion
de Angular.
children
est un accessoire spécial dans React et contiendra ce qui est à l'intérieur des balises de votre composant (ici à l' <App name={name}/>
intérieur Wrapper
, donc c'est lechildren
Notez que vous n'avez pas nécessairement besoin d'utiliser children
, ce qui est unique pour un composant, et vous pouvez également utiliser des accessoires normaux si vous le souhaitez, ou mélanger des accessoires et des enfants:
const AppLayout = ({header,footer,children}) => (
<div className="app">
<div className="header">{header}</div>
<div className="body">{children}</div>
<div className="footer">{footer}</div>
</div>
);
const appElement = (
<AppLayout
header={<div>header</div>}
footer={<div>footer</div>}
>
<div>body</div>
</AppLayout>
);
render(appElement,node);
C'est simple et adapté à de nombreux cas d'utilisation, et je le recommande pour la plupart des applications grand public.
rendre les accessoires
Il est possible de passer des fonctions de rendu à un composant, ce modèle est généralement appelé render prop
, et le children
prop est souvent utilisé pour fournir ce rappel.
Ce modèle n'est pas vraiment destiné à la mise en page. Le composant wrapper est généralement utilisé pour contenir et gérer un état et l'injecter dans ses fonctions de rendu.
Exemple de compteur:
const Counter = () => (
<State initial={0}>
{(val, set) => (
<div onClick={() => set(val + 1)}>
clicked {val} times
</div>
)}
</State>
);
Vous pouvez obtenir encore plus de fantaisie et même fournir un objet
<Promise promise={somePromise}>
{{
loading: () => <div>...</div>,
success: (data) => <div>{data.something}</div>,
error: (e) => <div>{e.message}</div>,
}}
</Promise>
Notez que vous n'avez pas forcément besoin d'utiliser children
, c'est une question de goût / API.
<Promise
promise={somePromise}
renderLoading={() => <div>...</div>}
renderSuccess={(data) => <div>{data.something}</div>}
renderError={(e) => <div>{e.message}</div>}
/>
À ce jour, de nombreuses bibliothèques utilisent des accessoires de rendu (contexte React, React-motion, Apollo ...) car les gens ont tendance à trouver cette API plus facile que celle des HOC. react-powerplug est une collection de composants de rendu simples. react-adopt vous aide à faire de la composition.
Composants d'ordre supérieur (HOC).
const wrapHOC = (WrappedComponent) => {
class Wrapper extends React.PureComponent {
render() {
return (
<div>
<div>header</div>
<div><WrappedComponent {...this.props}/></div>
<div>footer</div>
</div>
);
}
}
return Wrapper;
}
const App = ({name}) => <div>Hello {name}</div>;
const WrappedApp = wrapHOC(App);
render(<WrappedApp name="toto"/>,node);
Un composant d'ordre supérieur / HOC est généralement une fonction qui prend un composant et renvoie un nouveau composant.
L'utilisation d'un composant d'ordre supérieur peut être plus performante que l'utilisation de children
ou render props
, car le wrapper peut avoir la capacité de court-circuiter le rendu avec une longueur d'avance shouldComponentUpdate
.
Ici, nous utilisons PureComponent
. Lors du re-rendu de l'application, si le WrappedApp
nom de prop ne change pas au fil du temps, le wrapper a la capacité de dire "Je n'ai pas besoin de rendre parce que les accessoires (en fait, le nom) sont les mêmes qu'avant". Avec la children
solution basée ci-dessus, même si le wrapper est PureComponent
, ce n'est pas le cas car l'élément children est recréé à chaque fois que le parent est rendu, ce qui signifie que le wrapper sera probablement toujours rendu, même si le composant enveloppé est pur. Il existe un plugin babel qui peut aider à atténuer cela et à garantir un children
élément constant dans le temps.
Conclusion
Les composants d'ordre supérieur peuvent vous offrir de meilleures performances. Ce n'est pas si compliqué, mais cela semble certainement inamical au début.
Ne migrez pas toute votre base de code vers HOC après avoir lu ceci. N'oubliez pas que sur les chemins critiques de votre application, vous voudrez peut-être utiliser des HOC au lieu de wrappers d'exécution pour des raisons de performances, en particulier si le même wrapper est utilisé plusieurs fois, il vaut la peine d'en faire un HOC.
Redux a d'abord utilisé un wrapper d'exécution <Connect>
et est ensuite passé à un HOC connect(options)(Comp)
pour des raisons de performances (par défaut, le wrapper est pur et utilisé shouldComponentUpdate
). C'est l'illustration parfaite de ce que je voulais mettre en évidence dans cette réponse.
Notez que si un composant a une API de rendu-prop, il est généralement facile de créer un HOC par-dessus, donc si vous êtes un auteur de lib, vous devez d'abord écrire une API de prop de rendu, et éventuellement proposer une version HOC. C'est ce que fait Apollo avec le <Query>
composant render-prop, et le graphql
HOC qui l'utilise.
Personnellement, j'utilise les deux, mais en cas de doute je préfère les HOC car:
- Il est plus idiomatique de les composer (
compose(hoc1,hoc2)(Comp)
) par rapport aux accessoires de rendu
- Cela peut me donner de meilleures performances
- Je connais ce style de programmation
Je n'hésite pas à utiliser / créer des versions HOC de mes outils préférés:
- De React
Context.Consumer
comp
- Non déclaré
Subscribe
- en utilisant
graphql
HOC of Apollo au lieu de Query
render prop
A mon avis, parfois les accessoires de rendu rendent le code plus lisible, parfois moins ... J'essaye d'utiliser la solution la plus pragmatique en fonction des contraintes que j'ai. Parfois, la lisibilité est plus importante que les performances, parfois non. Choisissez judicieusement et ne suivez pas la tendance 2018 de tout convertir en accessoires de rendu.