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 transclusionAngular.
childrenjest 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ć childrenunikalnego 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 childrenlub 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 WrappedAppnazwa 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 childrenpowyż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 childrenelement 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 graphqluż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.Consumercomp
- Unstated's
Subscribe
- używając
graphqlHOC Apollo zamiast Queryrenderowania 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.