Za pomocą 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);
Jest to również znane jako transclusion
Angular.
children
jest specjalnym rekwizytem w React i będzie zawierał to, co jest w tagach twojego komponentu (tutaj <App name={name}/>
jest wewnątrz Wrapper
, więc to jestchildren
Pamiętaj, że niekoniecznie musisz używać children
unikalnego komponentu i możesz także użyć normalnych rekwizytów, jeśli chcesz, lub mieszać rekwizyty i dzieci:
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);
Jest to proste i odpowiednie dla wielu przypadków użycia, i polecam to dla większości aplikacji konsumenckich.
renderować rekwizyty
Możliwe jest przekazanie funkcji renderowania do komponentu, ten wzorzec jest ogólnie nazywany render prop
, achildren
rekwizyt jest często używany do zapewnienia tego wywołania zwrotnego.
Ten wzór nie jest tak naprawdę przeznaczony do układu. Komponent opakowania jest zwykle używany do przechowywania i zarządzania niektórymi stanami oraz wprowadzania ich w funkcje renderowania.
Przykład licznika:
const Counter = () => (
<State initial={0}>
{(val, set) => (
<div onClick={() => set(val + 1)}>
clicked {val} times
</div>
)}
</State>
);
Możesz uzyskać jeszcze więcej fantazji, a nawet zapewnić obiekt
<Promise promise={somePromise}>
{{
loading: () => <div>...</div>,
success: (data) => <div>{data.something}</div>,
error: (e) => <div>{e.message}</div>,
}}
</Promise>
Pamiętaj, że niekoniecznie musisz go używać children
, jest to kwestia gustu / API.
<Promise
promise={somePromise}
renderLoading={() => <div>...</div>}
renderSuccess={(data) => <div>{data.something}</div>}
renderError={(e) => <div>{e.message}</div>}
/>
Na dzień dzisiejszy wiele bibliotek używa rekwizytów renderujących (kontekst React, React-motion, Apollo ...), ponieważ ludzie uważają, że ten interfejs API jest łatwiejszy niż HOC. React-powerplug to zbiór prostych komponentów render-prop. Reaguj adoptuj pomaga tworzyć kompozycje.
Komponenty wyższego rzędu (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);
Składnik wyższego rzędu / HOC jest na ogół funkcją, która pobiera komponent i zwraca nowy komponent.
Użycie komponentu wyższego rzędu może być bardziej wydajne niż użycie children
lub render props
, ponieważ opakowanie może mieć możliwość zwarcia renderowania o krok do przodu shouldComponentUpdate
.
Tutaj korzystamy PureComponent
. Podczas ponownego renderowania aplikacji, jeśli WrappedApp
nazwa rekwizytu nie zmienia się w czasie, opakowanie może powiedzieć „Nie muszę renderować, ponieważ rekwizyty (właściwie nazwa) są takie same jak poprzednio”. W przypadku children
powyższego rozwiązania bazowego, nawet jeśli opakowanie jest PureComponent
, nie jest tak, ponieważ element potomny jest odtwarzany za każdym razem, gdy moduł nadrzędny renderuje, co oznacza, że opakowanie prawdopodobnie zawsze będzie ponownie renderowane, nawet jeśli opakowany komponent jest czysty. Istnieje wtyczka babel, która może to złagodzić i zapewnić stały children
element z czasem.
Wniosek
Komponenty wyższego rzędu mogą zapewnić lepszą wydajność. Nie jest to takie skomplikowane, ale z początku wygląda nieprzyjaźnie.
Nie migruj całej bazy kodu do HOC po przeczytaniu tego. Pamiętaj tylko, że ze względów wydajnościowych na krytycznych ścieżkach aplikacji możesz używać HOC zamiast owijek wykonawczych, szczególnie jeśli często używasz tego samego opakowania, warto rozważyć utworzenie HOC.
Redux używał początkowo opakowania środowiska wykonawczego, <Connect>
a później przełączył się na HOC connect(options)(Comp)
ze względu na wydajność (domyślnie opakowanie jest czyste i używa shouldComponentUpdate
). To idealna ilustracja tego, co chciałem podkreślić w tej odpowiedzi.
Uwaga: jeśli komponent ma API renderowania-prop, na ogół łatwo jest utworzyć na nim HOC, więc jeśli jesteś autorem lib, powinieneś najpierw napisać API renderowania-prop, a ostatecznie zaoferować wersję HOC. To właśnie robi Apollo z <Query>
komponentem render-prop i graphql
używającym go HOC.
Osobiście używam obu, ale w razie wątpliwości wolę HOC, ponieważ:
- Bardziej idiomatyczne jest komponowanie ich (
compose(hoc1,hoc2)(Comp)
) w porównaniu do renderowania rekwizytów
- Może dać mi lepsze wyniki
- Znam ten styl programowania
Nie waham się używać / tworzyć wersji HOC moich ulubionych narzędzi:
- React's
Context.Consumer
comp
- Unstated's
Subscribe
- używając
graphql
HOC Apollo zamiast Query
renderowania prop
Moim zdaniem, czasami renderowanie rekwizytów sprawia, że kod jest bardziej czytelny, a czasem mniej ... Staram się stosować najbardziej pragmatyczne rozwiązanie zgodnie z moimi ograniczeniami. Czasami czytelność jest ważniejsza niż występy, a czasem nie. Wybieraj mądrze i nie podążaj za trendem w 2018 r. Polegającym na przekształcaniu wszystkiego w render-rekwizyty.