Usando 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);
Isso também é conhecido como transclusion
em Angular.
children
é um suporte especial no React e conterá o que está dentro das tags do seu componente (aqui <App name={name}/>
está dentroWrapper
, então é ochildren
Observe que você não precisa necessariamente usar children
, que é exclusivo para um componente, e também pode usar adereços normais, se desejar, ou misturar adereços e filhos:
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);
Isso é simples e adequado para muitos casos de uso, e eu recomendo isso para a maioria dos aplicativos de consumo.
render adereços
É possível passar funções de renderização para um componente, esse padrão é geralmente chamado render prop
e ochildren
prop é frequentemente usado para fornecer esse retorno de chamada.
Esse padrão não é realmente destinado ao layout. O componente wrapper geralmente é usado para manter e gerenciar algum estado e injetá-lo em suas funções de renderização.
Exemplo de contador:
const Counter = () => (
<State initial={0}>
{(val, set) => (
<div onClick={() => set(val + 1)}>
clicked {val} times
</div>
)}
</State>
);
Você pode ficar ainda mais sofisticado e até fornecer um objeto
<Promise promise={somePromise}>
{{
loading: () => <div>...</div>,
success: (data) => <div>{data.something}</div>,
error: (e) => <div>{e.message}</div>,
}}
</Promise>
Observe que você não precisa necessariamente usar children
, é uma questão de gosto / API.
<Promise
promise={somePromise}
renderLoading={() => <div>...</div>}
renderSuccess={(data) => <div>{data.something}</div>}
renderError={(e) => <div>{e.message}</div>}
/>
Atualmente, muitas bibliotecas estão usando objetos de renderização (contexto de reação, movimento de reação, Apollo ...) porque as pessoas tendem a achar essa API mais fácil do que as HOCs. O react-powerplug é uma coleção de componentes simples de render-prop. reagir-adotar ajuda na composição.
Componentes de ordem superior (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);
Um componente de ordem superior / HOC geralmente é uma função que pega um componente e retorna um novo componente.
O uso de um componente de ordem superior pode ter mais desempenho do que o uso children
ou render props
, porque o wrapper pode ter a capacidade de causar um curto-circuito na renderização um passo à frente shouldComponentUpdate
.
Aqui estamos usando PureComponent
. Ao renderizar novamente o aplicativo, se o WrappedApp
nome prop não for alterado com o tempo, o wrapper poderá dizer "Não preciso renderizar porque os props (na verdade, o nome) são os mesmos de antes". Com a children
solução baseada acima, mesmo que o wrapper seja PureComponent
, não é o caso, porque o elemento filho é recriado toda vez que o pai é renderizado, o que significa que o wrapper provavelmente sempre será renderizado novamente, mesmo que o componente embrulhado seja puro. Existe um plugin babel que pode ajudar a mitigar isso e garantir um children
elemento constante ao longo do tempo.
Conclusão
Componentes de ordem superior podem oferecer melhor desempenho. Não é tão complicado, mas certamente parece hostil no começo.
Não migre toda a sua base de código para o HOC depois de ler isso. Lembre-se de que nos caminhos críticos do seu aplicativo você pode usar HOCs em vez de wrappers de tempo de execução por motivos de desempenho, principalmente se o mesmo wrapper for usado várias vezes, vale a pena considerar torná-lo um HOC.
O Redux usou inicialmente um wrapper de tempo de execução <Connect>
e depois mudou para um HOC connect(options)(Comp)
por motivos de desempenho (por padrão, o wrapper é puro e usado shouldComponentUpdate
). Esta é a ilustração perfeita do que eu queria destacar nesta resposta.
Observe que, se um componente possui uma API de renderização, geralmente é fácil criar um HOC sobre ela; portanto, se você é um autor da lib, deve primeiro criar uma API de renderização e, eventualmente, oferecer uma versão HOC. É isso que a Apollo faz com o <Query>
componente render-prop e o graphql
HOC usando-o.
Pessoalmente, uso os dois, mas em caso de dúvida prefiro HOCs porque:
- É mais idiomático compô-las (
compose(hoc1,hoc2)(Comp)
) em comparação com renderizações
- Pode me dar melhores desempenhos
- Eu estou familiarizado com este estilo de programação
Não hesito em usar / criar versões HOC das minhas ferramentas favoritas:
- React's
Context.Consumer
comp
- Não declarado
Subscribe
- usando
graphql
HOC da Apollo em vez de Query
render prop
Na minha opinião, às vezes os objetos de renderização tornam o código mais legível, às vezes menos ... Eu tento usar a solução mais pragmática de acordo com as restrições que tenho. Às vezes, a legibilidade é mais importante que as performances, às vezes não. Escolha sabiamente e não siga a tendência de 2018 de converter tudo em adereços de renderização.