Verwenden von 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);
Dies ist auch als transclusion
Angular bekannt.
children
ist eine spezielle Requisite in React und enthält, was sich in den Tags Ihrer Komponente befindet (hier <App name={name}/>
ist drin Wrapper
, also ist es daschildren
Beachten Sie, dass Sie nicht unbedingt verwenden müssen children
, was für eine Komponente eindeutig ist, und Sie können auch normale Requisiten verwenden, wenn Sie möchten, oder Requisiten und Kinder mischen:
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);
Dies ist für viele Anwendungsfälle einfach und in Ordnung, und ich würde dies für die meisten Consumer-Apps empfehlen.
Requisiten rendern
Es ist möglich, Renderfunktionen an eine Komponente zu übergeben. Dieses Muster wird im Allgemeinen aufgerufen render prop
, und die children
Requisite wird häufig verwendet, um diesen Rückruf bereitzustellen.
Dieses Muster ist nicht wirklich für das Layout gedacht. Die Wrapper-Komponente wird im Allgemeinen verwendet, um einen bestimmten Status zu halten, zu verwalten und in seine Renderfunktionen einzufügen.
Gegenbeispiel:
const Counter = () => (
<State initial={0}>
{(val, set) => (
<div onClick={() => set(val + 1)}>
clicked {val} times
</div>
)}
</State>
);
Sie können noch ausgefallener werden und sogar ein Objekt bereitstellen
<Promise promise={somePromise}>
{{
loading: () => <div>...</div>,
success: (data) => <div>{data.something}</div>,
error: (e) => <div>{e.message}</div>,
}}
</Promise>
Beachten Sie, dass Sie nicht unbedingt verwenden müssen children
, es ist eine Frage des Geschmacks / der API.
<Promise
promise={somePromise}
renderLoading={() => <div>...</div>}
renderSuccess={(data) => <div>{data.something}</div>}
renderError={(e) => <div>{e.message}</div>}
/>
Bis heute verwenden viele Bibliotheken Render-Requisiten (React-Kontext, React-Motion, Apollo ...), da diese API für Benutzer einfacher ist als für HOCs. React-Powerplug ist eine Sammlung einfacher Render-Prop-Komponenten. React-Adopt hilft Ihnen bei der Komposition.
Komponenten höherer Ordnung (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);
Eine Komponente höherer Ordnung / HOC ist im Allgemeinen eine Funktion, die eine Komponente übernimmt und eine neue Komponente zurückgibt.
Die Verwendung einer Komponente höherer Ordnung kann leistungsfähiger sein als die Verwendung von children
oder render props
, da der Wrapper das Rendering einen Schritt voraus kurzschließen kann shouldComponentUpdate
.
Hier verwenden wir PureComponent
. Wenn sich die WrappedApp
Namensstütze beim erneuten Rendern der App im Laufe der Zeit nicht ändert, kann der Wrapper sagen: "Ich muss nicht rendern, da die Requisiten (eigentlich der Name) dieselben wie zuvor sind." Mit der children
obigen basierten Lösung PureComponent
ist dies nicht der Fall , selbst wenn der Wrapper vorhanden ist , da das untergeordnete Element jedes Mal neu erstellt wird, wenn das übergeordnete Element gerendert wird. Dies bedeutet, dass der Wrapper wahrscheinlich immer neu gerendert wird, selbst wenn die umschlossene Komponente rein ist. Es gibt ein Babel-Plugin , das dazu beitragen kann, dies zu mildern und ein konstantes children
Element über die Zeit sicherzustellen .
Fazit
Komponenten höherer Ordnung können Ihnen eine bessere Leistung bieten. Es ist nicht so kompliziert, aber auf den ersten Blick sieht es auf jeden Fall unfreundlich aus.
Migrieren Sie nach dem Lesen nicht Ihre gesamte Codebasis zu HOC. Denken Sie daran, dass Sie auf kritischen Pfaden Ihrer App aus Leistungsgründen möglicherweise HOCs anstelle von Laufzeit-Wrappern verwenden möchten, insbesondere wenn derselbe Wrapper häufig verwendet wird. Es lohnt sich, darüber nachzudenken, ihn zu einem HOC zu machen.
Redux verwendete zunächst einen Laufzeit-Wrapper <Connect>
und wechselte später connect(options)(Comp)
aus Leistungsgründen zu einem HOC (standardmäßig ist der Wrapper rein und verwendet shouldComponentUpdate
). Dies ist die perfekte Illustration dessen, was ich in dieser Antwort hervorheben wollte.
Hinweis: Wenn eine Komponente über eine Render-Prop-API verfügt, ist es im Allgemeinen einfach, ein HOC darüber zu erstellen. Wenn Sie also ein lib-Autor sind, sollten Sie zuerst eine Render-Prop-API schreiben und schließlich eine HOC-Version anbieten. Dies ist, was Apollo mit der <Query>
Render-Prop-Komponente und dem graphql
HOC, das sie verwendet, macht.
Persönlich benutze ich beide, aber im Zweifelsfall bevorzuge ich HOCs, weil:
- Es ist idiomatischer, sie zu komponieren (
compose(hoc1,hoc2)(Comp)
) als Requisiten zu rendern
- Es kann mir bessere Leistungen geben
- Ich bin mit dieser Art der Programmierung vertraut
Ich zögere nicht, HOC-Versionen meiner Lieblingswerkzeuge zu verwenden / zu erstellen:
- React's
Context.Consumer
comp
- Nicht angegeben
Subscribe
- mit
graphql
HOC von Apollo anstelle von Query
Render Prop
Meiner Meinung nach machen Render-Requisiten den Code manchmal lesbarer, manchmal weniger ... Ich versuche, die pragmatischste Lösung gemäß den Einschränkungen zu verwenden, die ich habe. Manchmal ist Lesbarkeit wichtiger als Performances, manchmal nicht. Wählen Sie mit Bedacht und folgen Sie nicht dem Trend von 2018, alles in Render-Requisiten umzuwandeln.