ReactJS deux composants communicants
je viens de commencer avec ReactJS et je suis un peu coincé sur un problème que j'ai.
mon application est essentiellement une liste avec des filtres et un bouton pour changer la mise en page.
En ce moment j'utilise trois composants: <list />
, < Filters />
et <TopBar />
, maintenant évidemment quand je change les paramètres dans < Filters />
je veux déclencher une méthode dans <list />
pour mettre à jour ma vue.
Comment faire pour que ces 3 composants interagissent avec chacun autre, ou Ai-je besoin d'une sorte de modèle de données global où je peux juste apporter des modifications?
11 réponses
la meilleure approche dépend de la façon dont vous prévoyez organiser ces éléments. Quelques exemples de scénarios qui viennent à l'esprit:
-
<Filters />
est un composant enfant de<List />
- les deux
<Filters />
et<List />
sont des enfants d'un parent -
<Filters />
et<List />
vivent entièrement dans des composants racinaires séparés.
il peut y avoir d'autres scénarios auxquels je ne pense pas. Si le vôtre ne rentre pas dans ces limites, faites-le moi savoir. Voici quelques exemples très approximatifs de la façon dont j'ai géré les deux premiers scénarios:
scénario n ° 1
vous pouvez passer un gestionnaire de <List />
à <Filters />
, qui peut alors être appelé sur l'événement onChange
pour filtrer la liste avec la valeur courante.
/** @jsx React.DOM */
var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});
var List = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
var content;
if (displayedItems.length > 0) {
var items = displayedItems.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<h4>Results</h4>
{content}
</div>
);
}
});
React.renderComponent(<List />, document.body);
scénario 2
similaire au scénario #1, mais le composant parent sera celui qui passera la fonction handler à <Filters />
, et passera la liste filtrée à <List />
. Je préfère cette méthode puisqu'elle découple le <List />
du <Filters />
.
/** @jsx React.DOM */
var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});
var List = React.createClass({
render: function() {
var content;
if (this.props.items.length > 0) {
var items = this.props.items.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div className="results">
<h4>Results</h4>
{content}
</div>
);
}
});
var ListContainer = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<List items={displayedItems} />
</div>
);
}
});
React.renderComponent(<ListContainer />, document.body);
scénario n ° 3
lorsque les composants ne peuvent pas communiquer entre n'importe quelle sorte de relation parent-enfant, la documentation recommande la mise en place d'un système d'événements global .
il y a plusieurs façons de faire communiquer les composants. Certains peuvent être adaptés à votre cas d'utilisation. Voici une liste de quelques-uns que j'ai trouvé utiles de connaître.
Réagir
communication directe Parent / enfant
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}/>
)
}
}
ici, la composante enfant appellera un rappel fourni par le parent avec une valeur, et le parent sera en mesure d'obtenir la valeur fournie par les enfants dans le parent.
si vous construisez une fonctionnalité / page de votre application, il est préférable d'avoir un parent unique Gérer les callbacks/état (également appelé container
ou smart component
), et tous les enfants d'être apatrides, ne rapportant les choses au parent. De cette façon, vous pouvez facilement "partager" l'état du parent avec n'importe quel enfant qui en a besoin.
Contexte
le contexte React permet de maintenir l'État à la racine de la hiérarchie des composants, et d'injecter ceci Etablissez facilement les composants très imbriqués, sans avoir à passer les accessoires à tous les composants intermédiaires.
Jusqu'à présent, le contexte était une caractéristique expérimentale, mais une nouvelle API est disponible dans 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>
);
le consommateur utilise le render prop / children fonction pattern
Cochez cette blog post pour plus de détails.
avant React 16.3, je recommande d'utiliser react-broadcast qui offrent une API assez similaire, et d'utiliser L'ancienne API de contexte.
portails
utilisez un portail lorsque vous souhaitez garder 2 composants proches les uns des autres pour les faire communiquer avec des fonctions simples, comme dans parent / enfant normal, mais vous ne voulez pas que ces 2 composants aient une relation parent/enfant dans le DOM, en raison de / Les contraintes CSS qu'il implique (comme l'indice z, l'opacité...).
Dans ce cas, vous pouvez utiliser un "portail". Il existe différentes bibliothèques react en utilisant portails , généralement utilisé pour modals , popups, tooltips...
considérer ce qui suit:
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
pourrait produire la DOM suivante lorsqu'elle est rendue à l'intérieur de reactAppContainer
:
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
"1519100920 de" Slots
vous définissez une fente quelque part, et puis vous remplissez la fente d'un autre endroit de votre arbre de rendu.
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>
c'est un peu similaire aux portails sauf que le contenu rempli sera rendu dans un slot que vous définissez, alors que les portails rendent généralement un nouveau noeud dom (souvent un enfant de document.body)
Vérifier réagissent-fente de remplissage bibliothèque
"1519100920 de l'Événement" bus
comme indiqué dans la réaction documentation :
pour la communication entre deux composants qui n'ont pas de relation parent-enfant, vous pouvez configurer votre propre système d'événement global. Abonnez-vous à des événements dans componentDidMount(), désabonnez-vous dans componentWillUnmount(), et appelez setState() lorsque vous recevez un événement.
il y a beaucoup de choses que vous pouvez utiliser pour configurer un bus d'événements. Vous pouvez simplement créer un tableau d'auditeurs, et sur l'événement publier, tous les auditeurs recevraient l'événement. Ou vous pouvez utiliser quelque chose comme EventEmitter ou PostalJs
Flux
Flux est essentiellement un bus d'événements, sauf que les récepteurs d'événements sont des magasins. C'est similaire au système de bus d'événements de base sauf que L'état est géré en dehors de React
L'implémentation originale du Flux ressemble à une tentative de faire de l'Event-sourcing d'une manière hacky.
Redux est pour moi L'implémentation de Flux qui est la plus proche de l'event-sourcing, un avantage de nombreux avantages de l'event-sourcing comme la possibilité de voyager dans le temps. Il n'est pas strictement lié à React et peut également être utilisé avec d'autres vues fonctionnelles bibliothèque.
Egghead's Redux tutoriel vidéo est vraiment agréable et explique comment cela fonctionne à l'interne (il est vraiment simple).
Curseurs
Curseurs proviennent de ClojureScript / Om et largement utilisé dans les projets React. Ils permettent de gérer l'état en dehors de Réagir, et permettent à plusieurs composants ont accès en lecture/écriture à la même partie de la état, sans avoir besoin de savoir quoi que ce soit sur l'arbre des composants.
plusieurs implémentations existent, y compris immutable js , React-cursors et Omniscient "1519280920
Edit 2016 : il semble que les gens s'accordent à dire que les curseurs fonctionnent bien pour les applications plus petites, mais qu'ils ne se dimensionnent pas bien sur les applications complexes. Om Next n'a plus de curseurs (alors que C'est Om que introduction initiale du concept)
Elm architecture
le Elm architecture est une architecture proposée Pour être utilisée par le Elm language . Même si Elm n'est pas ReactJS, L'architecture Elm peut aussi être réalisée dans React.
Dan Abramov, l'auteur de Redux, a fait une mise en œuvre de l'architecture Elm en utilisant Réagir.
les deux Redux et Elm sont vraiment grands et ont tendance à renforcer les concepts d'event-sourcing sur le frontal, les deux permettant time-travel debugging, undo/redo, replay...
la principale différence entre Redux et Elm est que L'Elm a tendance à être beaucoup plus stricte sur la gestion de l'état. Dans Elm, vous ne pouvez pas avoir d'état de Composant local ou de crochets de montage/démontage et toutes les modifications de DOM doivent être déclenchées par des changements d'état globaux. L'architecture Elm propose une approche évolutive qui permet de traiter tout l'État à l'intérieur d'un seul objet immuable, tandis que Redux propose une approche qui vous invite à traiter la plupart de l'état dans un seul objet immuable.
alors que le modèle conceptuel de L'Elm est très élégant et que l'architecture permet de bien se dimensionner sur les grandes applications, il peut en pratique être difficile ou impliquer plus de boilerplate pour réaliser des tâches simples comme donner la priorité à une entrée après le montage, ou l'intégration avec une bibliothèque existante avec une interface impérative (ex: jQuery plugin). Lié à la question de .
aussi, l'architecture Elm implique plus de boilerplate de code. Ce n'est pas que verbeux ou compliqué à écrire, mais je pense que l'architecture Elm est plus adaptée aux langages statiquement typés.
PRF
Les bibliothèquescomme RxJS, BaconJS ou Kefir peuvent être utilisées pour produire des flux de PRF pour gérer la communication entre les composants.
Vous pouvez essayer par exemple Rx-Réagir
je pense que l'utilisation de ces libs est tout à fait similaire à l'utilisation de ce que la langue ELM offre avec signaux .
CycleJS cadre n'utilise pas ReactJS mais utilise des vdom . Il partage beaucoup de similitudes avec l'architecture Elm (mais est plus facile à utiliser dans la vie réelle parce qu'il permet des crochets vdom) et il utilise RxJS largement au lieu de fonctions, et peut être une bonne source d'inspiration si vous voulez utiliser FRP avec React. CycleJs tête d'oeuf vidéos sont agréables à comprendre comment il fonctionne.
CSP
CSP (Communication de Processus Séquentiels) sont actuellement populaire (surtout à cause des Aller/goroutines et le noyau.async / ClojureScript) mais vous pouvez les utiliser aussi en javascript avec JS-CSP .
James Long a fait une vidéo expliquant comment il peut être utilisé avec React.
Sagas
une saga est un concept d'arrière-plan issu du monde DDD / EventSourcing / CQRS, également appelé"process manager". Il est popularisé par le projet redux-saga , principalement en remplacement de redux-thunk pour le traitement des effets secondaires (appels API, etc.). La plupart des gens pensent actuellement qu'il ne s'agit que de services pour les effets secondaires, mais il est en fait plus sur les composants de découplage.
c'est plus un complément à une architecture de Flux (ou Redux) qu'un système de communication totalement nouveau, parce que la saga émet des actions de Flux à la fin. L'idée est que si vous avez widget1 et widget2, et que vous voulez qu'ils soient découplés, vous ne pouvez pas lancer l'action ciblant widget2 à partir de widget1. Si vous make widget1 ne lance que des actions qui se ciblent, et la saga est un "processus de fond" qui écoute les actions widget1, et peut envoyer des actions qui ciblent widget 2. La saga est le couplage entre les 2 widgets mais les widgets restent découplés.
si vous êtes intéressé jeter un oeil à ma réponse ici
Conclusion
Si vous voulez voir un exemple de l' même petite application en utilisant ces différents styles, vérifiez les branches de ce dépôt .
Je ne sais pas quelle est la meilleure option à long terme, mais J'aime vraiment comment Flux ressemble à l'event-sourcing.
si vous ne connaissez pas les concepts de sourcing d'événements, jetez un coup d'oeil à ce blog très pédagogique: Turning the database inside out with apache Samza , c'est un must-read to understand why Flux is nice (but cela pourrait également s'appliquer au PRF)
je pense que la communauté convient que la mise en œuvre de Flux la plus prometteuse est Redux , qui permettra progressivement une expérience de développeur très productive grâce à un rechargement à chaud. Impressionnant livecoding Ala Bret Victor Inventing on Principle video est possible!
C'est comme ça que j'ai géré ça.
Disons que vous avez un
les deux listes appartiennent à un troisième objet, le panneau de gauche. Les deux pour le tester, Copiez le code dans deux fichiers séparés et lancez l'index .html . Sélectionnez ensuite un mois et voyez comment le nombre de jours change. dates.js et le HTML pour exécuter le composant du panneau de gauche
de l'index.html
C'est un jeu avec les callbacks et les handlers dans le LeftPanel composant.
/** @jsx React.DOM */
var monthsLength = [0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var MONTHS_ARR = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
var DayNumber = React.createClass({
render: function() {
return (
<option value={this.props.dayNum}>{this.props.dayNum}</option>
);
}
});
var DaysList = React.createClass({
getInitialState: function() {
return {numOfDays: 30};
},
handleMonthUpdate: function(newMonthix) {
this.state.numOfDays = monthsLength[newMonthix];
console.log("Setting days to " + monthsLength[newMonthix] + " month = " + newMonthix);
this.forceUpdate();
},
handleDaySelection: function(evt) {
this.props.dateHandler(evt.target.value);
},
componentDidMount: function() {
this.props.readyCallback(this.handleMonthUpdate)
},
render: function() {
var dayNodes = [];
for (i = 1; i <= this.state.numOfDays; i++) {
dayNodes = dayNodes.concat([<DayNumber dayNum={i} />]);
}
return (
<select id={this.props.id} onChange = {this.handleDaySelection}>
<option value="" disabled defaultValue>Day</option>
{dayNodes}
</select>
);
}
});
var Month = React.createClass({
render: function() {
return (
<option value={this.props.monthIx}>{this.props.month}</option>
);
}
});
var MonthsList = React.createClass({
handleUpdate: function(evt) {
console.log("Local handler:" + this.props.id + " VAL= " + evt.target.value);
this.props.dateHandler(evt.target.value);
return false;
},
render: function() {
var monthIx = 0;
var monthNodes = this.props.data.map(function (month) {
monthIx++;
return (
<Month month={month} monthIx={monthIx} />
);
});
return (
<select id = {this.props.id} onChange = {this.handleUpdate}>
<option value="" disabled defaultValue>Month</option>
{monthNodes}
</select>
);
}
});
var LeftPanel = React.createClass({
dayRefresh: function(newMonth) {
// Nothing - will be replaced
},
daysReady: function(refreshCallback) {
console.log("Regisering days list");
this.dayRefresh = refreshCallback;
},
handleMonthChange: function(monthIx) {
console.log("New month");
this.dayRefresh(monthIx);
},
handleDayChange: function(dayIx) {
console.log("New DAY: " + dayIx);
},
render: function() {
return(
<div id="orderDetails">
<DaysList id="dayPicker" dateHandler={this.handleDayChange} readyCallback = {this.daysReady} />
<MonthsList data={MONTHS_ARR} id="monthPicker" dateHandler={this.handleMonthChange} />
</div>
);
}
});
React.renderComponent(
<LeftPanel />,
document.getElementById('leftPanel')
);
<!DOCTYPE html>
<html>
<head>
<title>Dates</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="//fb.me/react-0.11.1.js"></script>
<script src="//fb.me/JSXTransformer-0.11.1.js"></script>
</head>
<style>
#dayPicker {
position: relative;
top: 97px;
left: 20px;
width: 60px;
height: 17px;
}
#monthPicker {
position: relative;
top: 97px;
left: 22px;
width: 95px;
height: 17px;
}
select {
font-size: 11px;
}
</style>
<body>
<div id="leftPanel">
</div>
<script type="text/jsx" src="dates.js"></script>
</body>
</html>
j'ai vu que la question est déjà répondu, mais si vous souhaitez en savoir plus, il ya un total de 3 cas de communication entre les composants :
- Cas 1: communication Parent-enfant
- cas 2: communication Enfant-Parent
- Case 3: composants non liés (tout composant à un composant) communication
réponse D'extension de @MichaelLaCroix quand un scénario est que les composants ne peuvent pas communiquer entre n'importe quelle sorte de relation parent-enfant, la documentation recommande la mise en place d'un système d'événements global.
dans le cas de <Filters />
et <TopBar />
n'ont aucune des relations ci-dessus, un émetteur global simple pourrait être utilisé comme ceci:
componentDidMount
- abonnez-vous à l'événement
componentWillUnmount
- désabonnement de l'événement
EventSystem.js
class EventSystem{
constructor() {
this.queue = {};
this.maxNamespaceSize = 50;
}
publish(/** namespace **/ /** arguments **/) {
if(arguments.length < 1) {
throw "Invalid namespace to publish";
}
var namespace = arguments[0];
var queue = this.queue[namespace];
if (typeof queue === 'undefined' || queue.length < 1) {
console.log('did not find queue for %s', namespace);
return false;
}
var valueArgs = Array.prototype.slice.call(arguments);
valueArgs.shift(); // remove namespace value from value args
queue.forEach(function(callback) {
callback.apply(null, valueArgs);
});
return true;
}
subscribe(/** namespace **/ /** callback **/) {
const namespace = arguments[0];
if(!namespace) throw "Invalid namespace";
const callback = arguments[arguments.length - 1];
if(typeof callback !== 'function') throw "Invalid callback method";
if (typeof this.queue[namespace] === 'undefined') {
this.queue[namespace] = [];
}
const queue = this.queue[namespace];
if(queue.length === this.maxNamespaceSize) {
console.warn('Shifting first element in queue: `%s` since it reached max namespace queue count : %d', namespace, this.maxNamespaceSize);
queue.shift();
}
// Check if this callback already exists for this namespace
for(var i = 0; i < queue.length; i++) {
if(queue[i] === callback) {
throw ("The exact same callback exists on this namespace: " + namespace);
}
}
this.queue[namespace].push(callback);
return [namespace, callback];
}
unsubscribe(/** array or topic, method **/) {
let namespace;
let callback;
if(arguments.length === 1) {
let arg = arguments[0];
if(!arg || !Array.isArray(arg)) throw "Unsubscribe argument must be an array";
namespace = arg[0];
callback = arg[1];
}
else if(arguments.length === 2) {
namespace = arguments[0];
callback = arguments[1];
}
if(!namespace || typeof callback !== 'function') throw "Namespace must exist or callback must be a function";
const queue = this.queue[namespace];
if(queue) {
for(var i = 0; i < queue.length; i++) {
if(queue[i] === callback) {
queue.splice(i, 1); // only unique callbacks can be pushed to same namespace queue
return;
}
}
}
}
setNamespaceSize(size) {
if(!this.isNumber(size)) throw "Queue size must be a number";
this.maxNamespaceSize = size;
return true;
}
isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
}
NotificationComponent.js
class NotificationComponent extends React.Component {
getInitialState() {
return {
// optional. see alternative below
subscriber: null
};
}
errorHandler() {
const topic = arguments[0];
const label = arguments[1];
console.log('Topic %s label %s', topic, label);
}
componentDidMount() {
var subscriber = EventSystem.subscribe('error.http', this.errorHandler);
this.state.subscriber = subscriber;
}
componentWillUnmount() {
EventSystem.unsubscribe('error.http', this.errorHandler);
// alternatively
// EventSystem.unsubscribe(this.state.subscriber);
}
render() {
}
}
OK, il y a peu de façons de le faire, mais je veux exclusivement se concentrer sur l'utilisation de magasin en utilisant Redux qui rendent votre vie beaucoup plus facile pour cette situation plutôt que de vous donner une solution rapide que pour ce cas, l'utilisation de Pure React finira par tout gâcher dans la vraie grande application et la communication entre composants devient de plus en plus difficile que l'application se développe...
et Alors? Redux fait pour vous?
Redux est comme le stockage local dans votre application qui peut être utilisé chaque fois que vous avez besoin d'une donnée être utilisé à différents endroits dans votre application...
essentiellement idée de Redux vient de flux à l'origine, mais avec certains changements fondamentaux, y compris le concept d'avoir une source de vérité en créant un seul magasin...
regardez le graphique ci-dessous pour voir quelques différences entre Flux et Redux ...
envisager d'appliquer Redux dans votre application dès le début si votre application a besoin de communication entre les composants...
lire aussi ces mots de la Documentation de Redux pourrait être utile pour commencer:
comme les exigences pour JavaScript page unique les applications ont notre code doit gérer plus d'état que jamais avant . Cet état peut inclure les réponses du serveur et les données mises en cache., ainsi que des données locales qui n'ont pas encore été persisté à la serveur. L'état de L'assurance-chômage est également de plus en plus complexe, car nous devons Gérer les routes actives, les onglets sélectionnés, les spinners, les contrôles de pagination, et ainsi de suite.
gérer cet état en constante évolution est difficile. Si un modèle pouvez mettre à jour un autre modèle, une vue peut mettre à jour un modèle, qui met à jour un autre modèle, et cela, à son tour, pourrait causer une autre vue à mettre à jour. À certains point, vous ne comprenez plus ce qui se passe dans votre application que vous avez perte du contrôle sur le moment, le pourquoi et le comment de son état. Lorsqu'un système est opaque et non déterministe, il est difficile de reproduire des bogues ou ajouter nouveauté.
comme si ce n'était pas assez, considérer les nouvelles exigences de devenir commun dans développement de produits de première ligne. En tant que développeurs, nous sommes prévu pour gérer les mises à jour optimistes, le rendu côté serveur, la récupération données avant d'effectuer des transitions de route, et ainsi de suite. Nous nous trouvons en essayant de gérer une complexité que nous n'avons jamais eu à traiter avec avant, et nous posons inévitablement la question: Est-il temps d'abandonner? Le la réponse est non.
cette complexité est difficile à gérer car nous mélangeons deux concepts qui sont très difficile pour l'esprit humain raison à propos de: mutation et l'asynchronicité. Je les appelle Mentos et Coke. Les deux peuvent être grands en la séparation, mais ensemble, ils créent un désordre. Les bibliothèques comme Réagir essayer de résoudre ce problème dans la couche de vue en enlevant les deux manipulation asynchrone et DOM directe. Toutefois, la gestion de l'état de vos données sont à gauche jusqu'à vous. C'est ici que Redux entre.
suivant les étapes de Flux, CQRS, et Event Sourcing , Redux les tentatives de rendre prévisibles les mutations de l'état en imposant certaines restrictions sur comment et quand les mises à jour peuvent se produire . Ces restrictions sont reflétés dans les trois principes de Redux.
il y a une telle possibilité même si elles ne sont pas une relation Parent - enfant - et c'est un Flux. Il ya assez bonne (pour moi personnellement) la mise en œuvre pour ce appelé Alt.JS (avec Alt-Container).
par exemple, vous pouvez avoir une barre latérale qui dépend de ce qui est défini dans les détails du composant. Composant Sidebar est relié à SidebarActions et SidebarStore, tandis que les détails sont DetailsActions et DetailsStore.
vous pourriez utiliser alors AltContainer comme cela
<AltContainer stores={{
SidebarStore: SidebarStore
}}>
<Sidebar/>
</AltContainer>
{this.props.content}
qui garderait des magasins (bien je pourrais utiliser" magasin "au lieu de" magasins " prop). Maintenant, {ce.accessoires de jeu.contenu} peut être des détails selon la route. Disons que / Details nous redirigent vers cette vue. Les détails auraient par exemple une case à cocher qui changerait L'élément de la barre latérale de X à Y s'il était coché.
techniquement, il n'y a pas de relation entre eux et il serait difficile de se passer de flux. MAIS AVEC QU'il est plutôt facile.
passons maintenant aux actions de détail. Nous y créerons
class SiteActions {
constructor() {
this.generateActions(
'setSiteComponentStore'
);
}
setSiteComponent(value) {
this.dispatch({value: value});
}
}
et DetailsStore
class SiteStore {
constructor() {
this.siteComponents = {
Prop: true
};
this.bindListeners({
setSiteComponent: SidebarActions.COMPONENT_STATUS_CHANGED
})
}
setSiteComponent(data) {
this.siteComponents.Prop = data.value;
}
}
Et maintenant, c'est l'endroit où la magie commence.
comme vous pouvez le voir, il y a bindListener aux actions latérales.ComponentStatusChanged qui sera utilisé si setSiteComponent sera utilisé.
maintenant, en SidebarActions
componentStatusChanged(value){
this.dispatch({value: value});
}
Nous avons une telle chose. Il enverra cet objet sur appel. Et il sera appelé si setSiteComponent in store sera utilisé (que vous pouvez utiliser dans component par exemple pendant onChange on Button ot whatever)
maintenant dans SidebarStore nous aurons
constructor() {
this.structures = [];
this.bindListeners({
componentStatusChanged: SidebarActions.COMPONENT_STATUS_CHANGED
})
}
componentStatusChanged(data) {
this.waitFor(DetailsStore);
_.findWhere(this.structures[0].elem, {title: 'Example'}).enabled = data.value;
}
maintenant ici vous pouvez voir, qu'il attendra DetailsStore. Ça veut dire quoi? plus ou moins cela signifie que cette méthode doit attendre DetailsStoreto update avant de pouvoir mettre à jour m'.
tl; dr Un magasin est à l'écoute des méthodes dans un magasin, et déclenchera une action de component action, qui mettra à jour son propre magasin.
j'espère que ça peut vous aider.
si vous voulez explorer les options de communication entre les composants et se sentir comme il devient de plus en plus difficile, alors vous pourriez envisager d'adopter un bon modèle de conception: Flux .
il s'agit simplement d'un ensemble de règles qui définit comment vous stockez et mutez l'état d'application large, et utilisez cet état pour rendre des composants.
il y a de nombreuses implémentations de Flux, et l'implémentation officielle de Facebook en fait partie. Bien qu'il soit considéré comme celui qui contient le plus de code boilerplate, il est plus facile à comprendre puisque la plupart des choses sont explicites.
certaines des autres variantes sont flummox fluxxor fluxible et redux .
une fois, j'ai été là où vous êtes maintenant, en tant que débutant, vous parfois l'impression de sortir de la place sur la façon de réagir la façon de le faire. Je vais essayer de m'y prendre de la même façon que maintenant.
les États sont la pierre angulaire de la communication
habituellement, ce qui se résume est la façon dont vous modifiez les états dans ce composant dans votre cas, vous pointez trois composants.
<List />
: qui affichera probablement une liste des éléments en fonction d'un filtre
<Filters />
: Options de filtrage qui modifieront vos données.
<TopBar />
: liste des options.
pour orchestrer toute cette interaction, vous allez avoir besoin d'un composant plus élevé, appelons-le App, qui transmettra des actions et des données à chacun de ces composants afin, par exemple, qu'il ressemble à ceci
<div>
<List items={this.state.filteredItems}/>
<Filter filter={this.state.filter} setFilter={setFilter}/>
</div>
ainsi quand setFilter
est appelé il affectera le filteredItem et rejaillira les deux composants;. Dans ce cas n'est pas tout à fait clair je vous ai fait un exemple avec checkbox que vous pouvez vérifier dans un seul fichier:
import React, {Component} from 'react';
import {render} from 'react-dom';
const Person = ({person, setForDelete}) => (
<div>
<input type="checkbox" name="person" checked={person.checked} onChange={setForDelete.bind(this, person)} />
{person.name}
</div>
);
class PeopleList extends Component {
render() {
return(
<div>
{this.props.people.map((person, i) => {
return <Person key={i} person={person} setForDelete={this.props.setForDelete} />;
})}
<div onClick={this.props.deleteRecords}>Delete Selected Records</div>
</div>
);
}
} // end class
class App extends React.Component {
constructor(props) {
super(props)
this.state = {people:[{id:1, name:'Cesar', checked:false},{id:2, name:'Jose', checked:false},{id:3, name:'Marbel', checked:false}]}
}
deleteRecords() {
const people = this.state.people.filter(p => !p.checked);
this.setState({people});
}
setForDelete(person) {
const checked = !person.checked;
const people = this.state.people.map((p)=>{
if(p.id === person.id)
return {name:person.name, checked};
return p;
});
this.setState({people});
}
render () {
return <PeopleList people={this.state.people} deleteRecords={this.deleteRecords.bind(this)} setForDelete={this.setForDelete.bind(this)}/>;
}
}
render(<App/>, document.getElementById('app'));
le code suivant m'aide à configurer la communication entre deux frères et sœurs. La configuration se fait dans leur parent pendant les appels de render() et componentDidMount (). Il est basé sur https://reactjs.org/docs/refs-and-the-dom.html Espérons que cela aide.
class App extends React.Component<IAppProps, IAppState> {
private _navigationPanel: NavigationPanel;
private _mapPanel: MapPanel;
constructor() {
super();
this.state = {};
}
// `componentDidMount()` is called by ReactJS after `render()`
componentDidMount() {
// Pass _mapPanel to _navigationPanel
// It will allow _navigationPanel to call _mapPanel directly
this._navigationPanel.setMapPanel(this._mapPanel);
}
render() {
return (
<div id="appDiv" style={divStyle}>
// `ref=` helps to get reference to a child during rendering
<NavigationPanel ref={(child) => { this._navigationPanel = child; }} />
<MapPanel ref={(child) => { this._mapPanel = child; }} />
</div>
);
}
}
bizarrement, personne n'a mentionné mobx
. L'idée est similaire à redux
. Si j'ai une donnée à laquelle plusieurs composants sont abonnés, alors je peux utiliser cette donnée pour piloter plusieurs composants.