Esistono diversi modi per far comunicare i componenti. Alcuni possono essere adatti al tuo caso d'uso. Ecco un elenco di alcuni che ho trovato utile sapere.
Reagire
Comunicazione diretta genitore / figlio
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}/>
)
}
}
Qui il componente figlio chiamerà un callback fornito dal genitore con un valore e il genitore sarà in grado di ottenere il valore fornito dai figli nel genitore.
Se crei una funzione / pagina della tua app, è meglio avere un unico genitore che gestisce i callback / lo stato (anche chiamato container
o smart component
) e che tutti i figli siano apolidi, riportando solo cose al genitore. In questo modo puoi facilmente "condividere" lo stato del genitore con qualsiasi figlio che ne abbia bisogno.
Contesto
React Context consente di mantenere lo stato alla radice della gerarchia dei componenti ed essere in grado di iniettare facilmente questo stato in componenti annidati molto profondamente, senza la seccatura di dover passare oggetti di scena a ogni componente intermedio.
Fino ad ora, il contesto era una funzionalità sperimentale, ma in React 16.3 è disponibile una nuova API.
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>
);
Il consumatore sta usando il modello di funzione prop / children render
Controlla questo post sul blog per maggiori dettagli.
Prima di React 16.3, consiglierei di utilizzare la trasmissione broadcast che offre API abbastanza simili e di utilizzare l'API di contesto precedente.
portali
Utilizzare un portale quando si desidera mantenere vicini 2 componenti per farli comunicare con funzioni semplici, come nel normale genitore / figlio, ma non si desidera che questi 2 componenti abbiano una relazione padre / figlio nel DOM, perché dei vincoli visivi / CSS che implica (come z-index, opacità ...).
In questo caso è possibile utilizzare un "portale". Esistono diverse librerie di reazioni che utilizzano portali , generalmente utilizzate per i modali , popup, descrizioni comandi ...
Considera quanto segue:
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
Potrebbe produrre il seguente DOM quando eseguito il rendering all'interno reactAppContainer
:
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
Maggiori dettagli qui
slot
Definisci uno slot da qualche parte, quindi riempi lo slot da un altro punto dell'albero di rendering.
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>
Questo è un po 'simile ai portali, tranne per il fatto che il contenuto riempito verrà reso in uno slot definito dall'utente, mentre i portali generalmente renderizzano un nuovo nodo dom (spesso figlio di document.body)
Controlla la libreria di reazione-riempimento-slot
Bus di eventi
Come indicato nella documentazione di React :
Per la comunicazione tra due componenti che non hanno una relazione genitore-figlio, è possibile impostare il proprio sistema di eventi globale. Iscriviti agli eventi in componentDidMount (), annulla l'iscrizione in componentWillUnmount () e chiama setState () quando ricevi un evento.
Ci sono molte cose che puoi usare per configurare un bus eventi. Puoi semplicemente creare un array di listener e, in caso di pubblicazione di eventi, tutti i listener riceveranno l'evento. Oppure puoi usare qualcosa come EventEmitter o PostalJs
Flusso
Flusso è fondamentalmente un bus di eventi, tranne che i ricevitori di eventi sono negozi. Questo è simile al sistema di bus eventi di base, tranne per il fatto che lo stato è gestito al di fuori di React
L'implementazione di Flux originale sembra un tentativo di fare il sourcing degli eventi in modo confuso.
Redux è per me l'implementazione di Flux che è la più vicina al sourcing di eventi, un vantaggio per molti dei vantaggi del sourcing di eventi come la capacità di viaggiare nel tempo. Non è strettamente collegato a React e può essere utilizzato anche con altre librerie di viste funzionali.
Il video tutorial di Redhead di Egghead è davvero bello e spiega come funziona internamente (è davvero semplice).
Cursori
I cursori provengono da ClojureScript / Om e sono ampiamente utilizzati nei progetti React. Consentono di gestire lo stato al di fuori di React e consentono a più componenti di accedere in lettura / scrittura alla stessa parte dello stato, senza che sia necessario sapere nulla sull'albero dei componenti.
Esistono molte implementazioni, tra cui ImmutableJS , React-cursors e Omniscient
Modifica 2016 : sembra che le persone concordino che i cursori funzionano bene per le app più piccole, ma non si adatta bene alle app complesse. Om Next non ha più cursori (mentre è Om che ha introdotto il concetto inizialmente)
Architettura olmo
L' architettura Elm è un'architettura proposta per essere utilizzata dal linguaggio Elm . Anche se Elm non è ReactJS, l'architettura Elm può essere eseguita anche in React.
Dan Abramov, l'autore di Redux, ha realizzato un'implementazione dell'architettura Elm usando React.
Sia Redux che Elm sono davvero fantastici e tendono a potenziare i concetti di approvvigionamento di eventi sul frontend, entrambi consentendo il debug dei viaggi nel tempo, annulla / ripristina, riproduci ...
La differenza principale tra Redux ed Elm è che Elm tende ad essere molto più severo nella gestione dello stato. In Elm non è possibile avere lo stato del componente locale o hook di montaggio / smontaggio e tutte le modifiche al DOM devono essere attivate dalle modifiche dello stato globale. L'architettura Elm propone un approccio scalabile che consente di gestire TUTTO lo stato all'interno di un singolo oggetto immutabile, mentre Redux propone un approccio che invita a gestire la maggior parte dello stato in un singolo oggetto immutabile.
Mentre il modello concettuale di Elm è molto elegante e l'architettura consente di scalare bene su app di grandi dimensioni, in pratica può essere difficile o coinvolgere più boilerplate per realizzare compiti semplici come focalizzare un input dopo averlo montato o integrarsi con una libreria esistente con un'interfaccia imperativa (es. plugin JQuery). Problema correlato .
Inoltre, l'architettura Elm prevede più code di caldaia. Non è così prolisso o complicato da scrivere, ma penso che l'architettura Elm sia più adatta ai linguaggi tipizzati staticamente.
FRP
Librerie come RxJS, BaconJS o Kefir possono essere utilizzate per produrre flussi FRP per gestire la comunicazione tra i componenti.
Puoi provare ad esempio Rx-React
Penso che usare queste librerie sia abbastanza simile all'utilizzo di ciò che il linguaggio ELM offre con i segnali .
Il framework CycleJS non utilizza ReactJS ma utilizza vdom . Condivide molte somiglianze con l'architettura Elm (ma è più facile da usare nella vita reale perché consente gli hook vdom) e utilizza ampiamente gli RxJ invece delle funzioni e può essere una buona fonte di ispirazione se si desidera utilizzare FRP con Reagire. I video di CycleJs Egghead sono belli da capire come funziona.
CSP
I CSP (Comunicating Sequential Processes) sono attualmente popolari (principalmente a causa di Go / goroutines e core.async / ClojureScript) ma puoi usarli anche in javascript con JS-CSP .
James Long ha realizzato un video che spiega come può essere utilizzato con React.
Sagas
Una saga è un concetto di backend che viene dal mondo DDD / EventSourcing / CQRS, chiamato anche "gestore di processo". Viene reso popolare dal progetto redux-saga , principalmente in sostituzione di redux-thunk per la gestione degli effetti collaterali (ad esempio chiamate API ecc.). Molte persone attualmente pensano che serva solo per gli effetti collaterali, ma in realtà si tratta più di disaccoppiare i componenti.
È più un complimento per un'architettura Flux (o Redux) che un sistema di comunicazione totalmente nuovo, perché alla fine la saga emette azioni Flux. L'idea è che se si dispone di widget1 e widget2 e si desidera che siano disaccoppiati, non è possibile attivare l'azione di targeting widget2 da widget1. Quindi fai widget1 attivando solo le azioni che prendono di mira se stesso, e la saga è un "processo in background" che ascolta le azioni di widget1 e può inviare azioni destinate a widget2. La saga è il punto di accoppiamento tra i 2 widget, ma i widget rimangono disaccoppiati.
Se sei interessato dai un'occhiata alla mia risposta qui
Conclusione
Se vuoi vedere un esempio della stessa piccola app usando questi stili diversi, controlla i rami di questo repository .
Non so quale sia l'opzione migliore a lungo termine, ma mi piace molto come Flux assomigli al sourcing di eventi.
Se non conosci concetti di approvvigionamento di eventi, dai un'occhiata a questo blog molto pedagogico: capovolgendo il database con Apache Samza , è una lettura obbligata per capire perché Flux è carino (ma questo potrebbe valere anche per FRP )
Penso che la comunità sia d'accordo sul fatto che l'implementazione di Flux più promettente sia Redux , che consentirà progressivamente un'esperienza di sviluppo molto produttiva grazie alla ricarica a caldo. Impressionante livecoding ala video di Bret Victor Inventing on Principle è possibile!