Hay múltiples formas de hacer que los componentes se comuniquen. Algunos pueden ser adecuados para su caso de uso. Aquí hay una lista de algunos que he encontrado útiles para conocer.
Reaccionar
Comunicación directa padre / hijo
const Child = ({fromChildToParentCallback}) => (
<div onClick={() => fromChildToParentCallback(42)}>
Click me
</div>
);
class Parent extends React.Component {
receiveChildValue = (value) => {
console.log("Parent received value from child: " + value); // value is 42
};
render() {
return (
<Child fromChildToParentCallback={this.receiveChildValue}/>
)
}
}
Aquí el componente hijo llamará a una devolución de llamada proporcionada por el padre con un valor, y el padre podrá obtener el valor proporcionado por los niños en el padre.
Si crea una función / página de su aplicación, es mejor tener un padre único que administre las devoluciones de llamada / estado (también llamado container
o smart component
), y que todos los niños sean apátridas, solo informando cosas al padre. De esta manera, puede "compartir" fácilmente el estado del padre con cualquier hijo que lo necesite.
Contexto
React Context permite mantener el estado en la raíz de la jerarquía de sus componentes y poder inyectar este estado fácilmente en componentes muy anidados, sin la molestia de tener que pasar accesorios a cada componente intermedio.
Hasta ahora, el contexto era una característica experimental, pero una nueva API está disponible en React 16.3.
const AppContext = React.createContext(null)
class App extends React.Component {
render() {
return (
<AppContext.Provider value={{language: "en",userId: 42}}>
<div>
...
<SomeDeeplyNestedComponent/>
...
</div>
</AppContext.Provider>
)
}
};
const SomeDeeplyNestedComponent = () => (
<AppContext.Consumer>
{({language}) => <div>App language is currently {language}</div>}
</AppContext.Consumer>
);
El consumidor está utilizando el patrón de función render prop / children
Consulte esta publicación de blog para más detalles.
Antes de React 16.3, recomendaría usar react-broadcast, que ofrece una API bastante similar, y una API de contexto anterior.
Portales
Use un portal cuando desee mantener 2 componentes juntos para que se comuniquen con funciones simples, como en padre / hijo normal, pero no desea que estos 2 componentes tengan una relación padre / hijo en el DOM, porque de las restricciones visuales / CSS que implica (como el índice z, la opacidad ...).
En este caso puede usar un "portal". Existen diferentes bibliotecas de reacción que utilizan portales , generalmente utilizados para modales , ventanas emergentes, información sobre herramientas ...
Considera lo siguiente:
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
Podría producir el siguiente DOM cuando se representa en el interior reactAppContainer
:
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
Más detalles aquí
Tragamonedas
Define una ranura en algún lugar y luego llena la ranura desde otro lugar de su árbol de renderizado.
import { Slot, Fill } from 'react-slot-fill';
const Toolbar = (props) =>
<div>
<Slot name="ToolbarContent" />
</div>
export default Toolbar;
export const FillToolbar = ({children}) =>
<Fill name="ToolbarContent">
{children}
</Fill>
Esto es un poco similar a los portales, excepto que el contenido lleno se representará en un espacio que usted defina, mientras que los portales generalmente representan un nuevo nodo dom (a menudo un elemento secundario de document.body)
Verifique la biblioteca react-slot-fill
Bus de eventos
Como se indica en la documentación de React :
Para la comunicación entre dos componentes que no tienen una relación padre-hijo, puede configurar su propio sistema global de eventos. Suscríbase a los eventos en componentDidMount (), anule la suscripción en componentWillUnmount () y llame a setState () cuando reciba un evento.
Hay muchas cosas que puede usar para configurar un bus de eventos. Puede crear una variedad de oyentes, y en la publicación del evento, todos los oyentes recibirían el evento. O puede usar algo como EventEmitter o PostalJs
Flujo
Flux es básicamente un bus de eventos, excepto que los receptores de eventos son tiendas. Esto es similar al sistema de bus de eventos básico, excepto que el estado se administra fuera de React
La implementación original de Flux se parece a un intento de hacer un abastecimiento de eventos de una manera hacky.
Redux es para mí la implementación de Flux que es la más cercana al abastecimiento de eventos, y beneficia muchas de las ventajas del abastecimiento de eventos, como la capacidad de viajar en el tiempo. No está estrictamente vinculado a React y también se puede usar con otras bibliotecas de vistas funcionales.
El video tutorial de Redhead de Egghead es realmente agradable y explica cómo funciona internamente (es realmente simple).
Cursores
Los cursores provienen de ClojureScript / Om y se usan ampliamente en proyectos React. Permiten administrar el estado fuera de React y permiten que múltiples componentes tengan acceso de lectura / escritura a la misma parte del estado, sin necesidad de saber nada sobre el árbol de componentes.
Existen muchas implementaciones, incluidas ImmutableJS , React-cursores y Omniscient
Editar 2016 : parece que las personas están de acuerdo en que los cursores funcionan bien para aplicaciones más pequeñas, pero no se ajustan bien en aplicaciones complejas. Om Next ya no tiene cursores (mientras que Om introdujo el concepto inicialmente)
Arquitectura de olmo
La arquitectura Elm es una arquitectura propuesta para ser utilizada por el lenguaje Elm . Incluso si Elm no es ReactJS, la arquitectura de Elm también se puede hacer en React.
Dan Abramov, el autor de Redux, hizo una implementación de la arquitectura Elm usando React.
Tanto Redux como Elm son realmente geniales y tienden a potenciar los conceptos de origen de eventos en la interfaz, lo que permite la depuración de viajes en el tiempo, deshacer / rehacer, reproducir ...
La principal diferencia entre Redux y Elm es que Elm tiende a ser mucho más estricto sobre la gestión del estado. En Elm no puede tener un estado de componente local o montar / desmontar ganchos y todos los cambios de DOM deben ser activados por cambios de estado globales. La arquitectura Elm propone un enfoque escalable que permite manejar TODO el estado dentro de un único objeto inmutable, mientras que Redux propone un enfoque que lo invita a manejar la MAYORÍA del estado en un solo objeto inmutable.
Si bien el modelo conceptual de Elm es muy elegante y la arquitectura permite escalar bien en aplicaciones grandes, en la práctica puede ser difícil o involucrar más repetitivo para lograr tareas simples como enfocar una entrada después de montarla o integrarse con una biblioteca existente con una interfaz imperativa (es decir, el complemento JQuery). Tema relacionado .
Además, la arquitectura Elm implica más código repetitivo. No es tan detallado o complicado de escribir, pero creo que la arquitectura Elm es más adecuada para los lenguajes de tipo estático.
FRP
Las bibliotecas como RxJS, BaconJS o Kefir se pueden usar para producir flujos FRP para manejar la comunicación entre componentes.
Puedes probar por ejemplo Rx-React
Creo que usar estas bibliotecas es bastante similar a usar lo que ofrece el lenguaje ELM con señales .
El marco de CycleJS no usa ReactJS pero usa vdom . Comparte muchas similitudes con la arquitectura Elm (pero es más fácil de usar en la vida real porque permite ganchos vdom) y usa RxJ ampliamente en lugar de funciones, y puede ser una buena fuente de inspiración si desea usar FRP con Reaccionar. Los videos de CycleJs Egghead son agradables de entender cómo funciona.
CSP
Los CSP (procesos secuenciales de comunicación) son populares actualmente (principalmente debido a Go / goroutines y core.async / ClojureScript), pero también puede usarlos en JavaScript con JS-CSP .
James Long ha hecho un video explicando cómo se puede usar con React.
Sagas
Una saga es un concepto de fondo que proviene del mundo DDD / EventSourcing / CQRS, también llamado "administrador de procesos". Está siendo popularizado por el proyecto redux-saga , principalmente como un reemplazo de redux-thunk para el manejo de efectos secundarios (es decir, llamadas API, etc.). La mayoría de las personas actualmente piensa que solo sirve para los efectos secundarios, pero en realidad se trata más de desacoplar componentes.
Es más un complemento de una arquitectura Flux (o Redux) que un sistema de comunicación totalmente nuevo, porque la saga emite acciones Flux al final. La idea es que si tiene widget1 y widget2, y desea que estén desacoplados, no puede disparar la acción de orientación widget2 desde widget1. Por lo tanto, hace que widget1 solo active acciones que se dirigen a sí mismo, y la saga es un "proceso en segundo plano" que escucha las acciones de widget1 y puede enviar acciones que se dirigen a widget2. La saga es el punto de acoplamiento entre los 2 widgets, pero los widgets permanecen desacoplados.
Si estás interesado mira mi respuesta aquí
Conclusión
Si desea ver un ejemplo de la misma pequeña aplicación que utiliza estos diferentes estilos, consulte las ramas de este repositorio .
No sé cuál es la mejor opción a largo plazo, pero realmente me gusta cómo se ve Flux como fuente de eventos.
Si no conoce los conceptos de abastecimiento de eventos, eche un vistazo a este blog muy pedagógico: dar la vuelta a la base de datos con apache Samza , es una lectura obligada para comprender por qué Flux es bueno (pero esto también podría aplicarse a FRP) )
Creo que la comunidad está de acuerdo en que la implementación de Flux más prometedora es Redux , que permitirá progresivamente una experiencia de desarrollo muy productiva gracias a la recarga en caliente. ¡Impresionante livecoding de ala Bret Victor's Inventing on Principle es posible!