Utilizando 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);
Esto también se conoce como transclusion
en Angular.
children
es un accesorio especial en React y contendrá lo que está dentro de las etiquetas de sus componentes (aquí <App name={name}/>
está dentro Wrapper
, por lo que es elchildren
Tenga en cuenta que no necesariamente necesita usar children
, que es único para un componente, y también puede usar accesorios normales si lo desea, o mezclar accesorios y niños:
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);
Esto es simple y está bien para muchos casos de uso, y lo recomendaría para la mayoría de las aplicaciones de consumo.
renderizar accesorios
Es posible pasar funciones de renderizado a un componente, este patrón generalmente se llama render prop
, y el children
accesorio se usa a menudo para proporcionar esa devolución de llamada.
Este patrón no está destinado realmente para el diseño. El componente contenedor generalmente se usa para mantener y administrar algún estado e inyectarlo en sus funciones de representación.
Ejemplo de contador:
const Counter = () => (
<State initial={0}>
{(val, set) => (
<div onClick={() => set(val + 1)}>
clicked {val} times
</div>
)}
</State>
);
Puede ser aún más elegante e incluso proporcionar un objeto
<Promise promise={somePromise}>
{{
loading: () => <div>...</div>,
success: (data) => <div>{data.something}</div>,
error: (e) => <div>{e.message}</div>,
}}
</Promise>
Tenga en cuenta que no necesariamente necesita usar children
, es una cuestión de gusto / API.
<Promise
promise={somePromise}
renderLoading={() => <div>...</div>}
renderSuccess={(data) => <div>{data.something}</div>}
renderError={(e) => <div>{e.message}</div>}
/>
A partir de hoy, muchas bibliotecas están utilizando accesorios de renderizado (React context, React-motion, Apollo ...) porque las personas tienden a encontrar esta API más fácil que HOC. react-powerplug es una colección de componentes simples de render-prop. react-adopt te ayuda a hacer la composición.
Componentes de orden 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);
Un Componente / HOC de orden superior es generalmente una función que toma un componente y devuelve un nuevo componente.
Usar un componente de orden superior puede ser más eficaz que usar children
o render props
, porque el contenedor puede tener la capacidad de cortocircuitar el renderizado un paso adelante shouldComponentUpdate
.
Aquí lo estamos usando PureComponent
. Al volver a representar la aplicación, si el WrappedApp
nombre del accesorio no cambia con el tiempo, el contenedor tiene la capacidad de decir "No necesito renderizar porque los accesorios (en realidad, el nombre) son los mismos que antes". Con la children
solución basada arriba, incluso si la envoltura es PureComponent
, no es el caso porque el elemento hijo se recrea cada vez que se procesa el padre, lo que significa que la envoltura probablemente siempre se volverá a procesar, incluso si el componente envuelto es puro. Hay un complemento de babel que puede ayudar a mitigar esto y garantizar un children
elemento constante a lo largo del tiempo.
Conclusión
Los componentes de orden superior pueden brindarle un mejor rendimiento. No es tan complicado, pero al principio parece poco amigable.
No migre toda su base de código a HOC después de leer esto. Solo recuerde que en las rutas críticas de su aplicación es posible que desee usar HOC en lugar de envoltorios de tiempo de ejecución por razones de rendimiento, particularmente si el mismo contenedor se usa muchas veces, vale la pena considerar convertirlo en un HOC.
Redux usó al principio un contenedor de tiempo de ejecución <Connect>
y luego cambió a un HOC connect(options)(Comp)
por razones de rendimiento (de manera predeterminada, el contenedor es puro y está en uso shouldComponentUpdate
). Esta es la ilustración perfecta de lo que quería resaltar en esta respuesta.
Tenga en cuenta que si un componente tiene una API de render-prop, generalmente es fácil crear un HOC encima de él, por lo que si es un autor de lib, primero debe escribir una API de render prop y, finalmente, ofrecer una versión de HOC. Esto es lo que hace Apollo con el <Query>
componente render-prop, y el graphql
HOC que lo usa.
Personalmente, uso ambos, pero en caso de duda prefiero los HOC porque:
- Es más idiomático componerlos (
compose(hoc1,hoc2)(Comp)
) en comparación con los accesorios de renderizado
- Me puede dar mejores actuaciones
- Estoy familiarizado con este estilo de programación.
No dudo en usar / crear versiones HOC de mis herramientas favoritas:
- Comp de
Context.Consumer
reacción
- Sin declarar
Subscribe
- usando
graphql
HOC de Apollo en lugar de Query
render prop
En mi opinión, a veces los accesorios de render hacen que el código sea más legible, a veces menos ... Intento usar la solución más pragmática de acuerdo con las restricciones que tengo. A veces, la legibilidad es más importante que las actuaciones, a veces no. Elija sabiamente y no siga la tendencia de 2018 de convertir todo en accesorios de renderizado.