Використання 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. react-powerplug - це сукупність простих рендеринг-опорних компонентів. реагувати-приймати допомагає робити композицію.
Компоненти вищого порядку (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
вказівник імені не змінюється з часом, обгортка має можливість сказати: "Мені не потрібно робити рендерінг, оскільки реквізити (власне, ім'я) такі ж, як і раніше". З children
вищенаведеним рішенням, навіть якщо обгортка є PureComponent
, це не так, тому що дочірній елемент відтворюється кожного разу, коли батьківський показ відображається, а це означає, що обгортка, ймовірно, завжди буде повторно відображатися, навіть якщо загорнута складова чиста. Існує плагін Babel, який може допомогти пом’якшити це і забезпечити постійний children
елемент у часі.
Висновок
Компоненти вищого порядку можуть забезпечити кращі показники роботи. Це не так складно, але, звичайно, спочатку виглядає недоброзичливо.
Не переносьте всю свою кодову базу до HOC після прочитання цього. Пам’ятайте лише, що на критичних шляхах вашої програми ви можете використовувати HOC замість обертових оболонок для виконання з міркувань продуктивності, особливо якщо та сама обгортка використовується багато разів, варто розглянути її як HOC.
Redux спочатку використовував обгортку під час виконання, <Connect>
а згодом перейшов до HOC connect(options)(Comp)
з міркувань продуктивності (за замовчуванням, обгортка чиста і використовується shouldComponentUpdate
). Це ідеальна ілюстрація того, що я хотів висвітлити у цій відповіді.
Зверніть увагу, якщо компонент має API рендеринга, створити HOC поверх нього, як правило, легко, тому якщо ви є автором lib, спочатку слід написати API підтримки рендерінгу та, зрештою, запропонувати версію HOC. Це те, що робить Apollo з <Query>
компонентом рендеринга і graphql
HOC, що використовує його.
Особисто я використовую і те, і інше, але коли сумніваюся, я віддаю перевагу HOCs тому, що:
- Більш ідіоматичним є їх складання (
compose(hoc1,hoc2)(Comp)
) порівняно з рендерінгами
- Це може дати мені кращі виступи
- Мені знайомий цей стиль програмування
Я не вагаюся використовувати / створювати HOC версії моїх улюблених інструментів:
- Реакція
Context.Consumer
комп
- Нестандартні
Subscribe
- використовуючи
graphql
HOC Apollo замість Query
надання опори
На мою думку, іноді рендери рендерінгу роблять код більш читабельним, іноді менше ... Я намагаюся використовувати найбільш прагматичне рішення відповідно до обмежень, які у мене є. Іноді читабельність важливіша за продуктивність, іноді ні. Вибирайте розумно і не зобов’язуйтесь слідувати тенденції 2018 року перетворювати все на рендеринг.