С помощью 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);
Это также известно как transclusion
в Angular.
children
является специальной опорой в React и будет содержать то, что находится внутри тегов вашего компонента (здесь <App name={name}/>
внутри Wrapper
, так что этоchildren
Обратите внимание, что вам не обязательно использовать его children
, который является уникальным для компонента, и вы можете использовать обычные реквизиты, если хотите, или смешивать реквизиты и дочерние элементы:
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);
Это просто и хорошо для многих случаев использования, и я рекомендую это для большинства потребительских приложений.
сделать реквизит
Можно передать функции рендеринга компоненту, этот шаблон обычно вызывается render prop
, и children
реквизит часто используется для обеспечения этого обратного вызова.
Этот шаблон не предназначен для макета. Компонент-обертка обычно используется для хранения и управления некоторым состоянием и добавления его в функции рендеринга.
Встречный пример:
const Counter = () => (
<State initial={0}>
{(val, set) => (
<div onClick={() => set(val + 1)}>
clicked {val} times
</div>
)}
</State>
);
Вы можете получить еще больше фантазии и даже предоставить объект
<Promise promise={somePromise}>
{{
loading: () => <div>...</div>,
success: (data) => <div>{data.something}</div>,
error: (e) => <div>{e.message}</div>,
}}
</Promise>
Обратите внимание, что вам не обязательно использовать children
, это вопрос вкуса / API.
<Promise
promise={somePromise}
renderLoading={() => <div>...</div>}
renderSuccess={(data) => <div>{data.something}</div>}
renderError={(e) => <div>{e.message}</div>}
/>
На сегодняшний день многие библиотеки используют реквизит рендеринга (контекст React, React-motion, Apollo ...), потому что люди склонны находить этот API проще, чем HOC. response-powerplug - это набор простых компонентов render-prop. Реакция-принятие помогает вам сделать композицию.
Компоненты высшего порядка (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);
Higher-Order Компонент / HOC , как правило , функция , которая принимает компонент и возвращает новый компонент.
Использование компонента высшего порядка может быть более производительным, чем использование children
или render props
, потому что оболочка может иметь возможность замкнуть рендеринг на шаг впереди shouldComponentUpdate
.
Здесь мы используем PureComponent
. При повторном рендеринге приложения, если WrappedApp
имя prop не изменяется со временем, оболочка может сказать: «Мне не нужно рендерить, потому что реквизиты (фактически, имя) такие же, как и раньше». При использовании children
приведенного выше решения, даже если это оболочка PureComponent
, это не так, потому что дочерний элемент воссоздается каждый раз, когда создается родительский элемент, что означает, что оболочка, вероятно, всегда будет повторно отображаться, даже если упакованный компонент будет чистым. Существует плагин Babel, который может помочь смягчить это и обеспечить постоянный children
элемент с течением времени.
Вывод
Компоненты высшего порядка могут дать вам лучшую производительность. Это не так сложно, но поначалу это выглядит недружелюбно.
Не переносите всю кодовую базу в HOC после прочтения этого. Просто помните, что на критических путях вашего приложения вы можете захотеть использовать HOC вместо оболочек времени выполнения по соображениям производительности, особенно если одна и та же оболочка используется много раз, стоит подумать о том, чтобы сделать ее HOC.
Redux сначала использовал оболочку времени выполнения, <Connect>
а затем переключился на HOC connect(options)(Comp)
по соображениям производительности (по умолчанию оболочка чистая и используется shouldComponentUpdate
). Это отличная иллюстрация того, что я хотел выделить в этом ответе.
Обратите внимание, что если у компонента есть API рендеринга-пропа, поверх него обычно легко создать HOC, поэтому, если вы являетесь автором lib, вы должны сначала написать API-интерфейс рендеринга и в конечном итоге предложить версию HOC. Это то, что Apollo делает с <Query>
компонентом render-prop и с graphql
помощью HOC.
Лично я использую оба, но когда сомневаюсь, я предпочитаю HOC, потому что:
- Составлять их (
compose(hoc1,hoc2)(Comp)
) более идиоматично по сравнению с реквизитами рендеринга
- Это может дать мне лучшие показатели
- Я знаком с этим стилем программирования
Я без колебаний использую / создаю HOC версии моих любимых инструментов:
- Реакт
Context.Consumer
комп
- неустановленные-х
Subscribe
- используя
graphql
HOC Аполлона вместо Query
рендера проп
На мой взгляд, иногда реквизит рендеринга делает код более читабельным, иногда меньше ... Я стараюсь использовать наиболее прагматичное решение в соответствии с имеющимися у меня ограничениями. Иногда читаемость важнее исполнения, иногда нет. Выбирайте мудро и не следуйте тенденции 2018 года по конвертации всего в рендер-реквизит.