Je suis nouveau sur React.js et j'ai du mal à comprendre quelques concepts de base pour décider si nous devons utiliser cette bibliothèque pour notre application. Mon problème principal est en fait la gestion des mises à jour dans le modèle récupéré depuis le serveur.
Imaginez que j'ai une page qui devrait afficher cinq modèles différents. Je l'ai construit de la manière décrite dans cet article: http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html , donc je ont un composant "racine" où tous les 5 modèles ont réussi et en utilisant des accessoires, ils descendent vers les composants qui contiennent ces modèles. Donc, maintenant 2 modèles mis à jour (j'obtiens ces événements de mon code de modèle qui vivent en dehors des composants React) et je dois le refléter sur l'interface utilisateur. Quelle est la meilleure façon de procéder?
Je pense aux options suivantes:
Merci d'avance et j'espère avoir pu expliquer clairement mon problème.
Appeler à nouveau renderComponent avec le même composant mais des données différentes équivaut à appeler component.setProps (). Donc, soit conservez tous les modèles comme état dans le dénominateur le moins commun, soit appelez simplement setProps/renderComponent à nouveau lorsqu'il change.
Pour le moment, je connais au moins trois façons de transmettre de nouvelles données à un composant:
Pour la troisième option, je me suis inspiré de la réponse de StevenH et je l'ai un peu étendu. Veuillez vérifier mon implémentation sur j sfiddle.net/kb3gN/12002/ .
var Data = { value: 1 };
var dataChange = function(callback){
if(callback){
callback(Data);
setInterval(function(){
Data.value++;
callback(Data);
}, 1000);
}
return Data;
};
var World = React.createClass({
render: function() {
return <strong>{this.props.data.value}</strong>;
}
});
var Hello = React.createClass({
getInitialState: function() {
return {
data: this.props.dataChange()
};
},
componentDidMount: function() {
this.props.dataChange(this.updateHandler)
},
updateHandler: function(data) {
this.setState({
data: data
});
},
render: function() {
return (
<div>
Value: <World data={this.state.data} />
</div>
);
}
});
React.renderComponent(<Hello dataChange={dataChange} />, document.body);
Il existe également une version étendue sur jsfiddle.net/kb3gN/12007 .
function ListenersService(){
var listeners = {};
this.addListener = function(callback){
var id;
if(typeof callback === 'function'){
id = Math.random().toString(36).slice(2);
listeners[id] = callback;
}
return id;
}
this.removeListener = function( id){
if(listeners[id]){
delete listeners[id];
return true;
}
return false;
}
this.notifyListeners = function(data){
for (var id in listeners) {
if(listeners.hasOwnProperty(id)){
listeners[id](data);
}
}
}
}
function DataService(ListenersService){
var Data = { value: 1 };
var self = this;
var listenersService = new ListenersService();
this.addListener = listenersService.addListener;
this.removeListener = listenersService.removeListener;
this.getData = function(){
return Data;
}
setInterval(function(){
Data.value++;
listenersService.notifyListeners(Data);
}, 1000);
}
var dataSevice = new DataService(ListenersService);
var World = React.createClass({
render: function() {
return <strong>{this.props.data.value}</strong>;
}
});
var Hello = React.createClass({
getInitialState: function() {
return {
data: this.props.dataService.getData()
};
},
componentDidMount: function() {
this.props.dataService.addListener(this.updateHandler)
},
updateHandler: function(data) {
this.setState({
data: data
});
},
render: function() {
return (
<div>
Value: <World data={this.state.data} />
</div>
);
}
});
React.renderComponent(<Hello dataService={dataSevice} />, document.body);
Cette implémentation ne suit pas complètement l'idée de composants isolés (car le composant Hello dépend de l'API DataService), mais elle peut être plus abstraite et dépend du développeur de l'application quelles conventions spécifiques à l'application ses composants suivront. Par exemple, voir le mélange des premier et deuxième exemples sur jsfiddle.net/kb3gN/12015 (objet halloDataStatic et rappel halloDataDynamic)
Remarque: Le ListenersService utilisé dans l'exemple suit le modèle d'observateur et le modèle lui-même a plus d'inconvénients que d'avantages dans de nombreux scénarios. Mais à côté de cela, ce que je voulais montrer avec ces exemples, c'est qu'il existe un moyen de liaison de données avec un rappel
<div id="static"></div>
<div id="dynamic"></div>
<script>
function ListenersService(){
var listeners = {};
this.addListener = function(callback){
var id;
if(typeof callback === 'function'){
id = Math.random().toString(36).slice(2);
listeners[id] = callback;
}
return id;
}
this.removeListener = function( id){
if(listeners[id]){
delete listeners[id];
return true;
}
return false;
}
this.notifyListeners = function(data){
for (var id in listeners) {
if(listeners.hasOwnProperty(id)){
listeners[id](data);
}
}
}
}
function DataService(ListenersService){
var Data = { value: 1 };
var self = this;
var listenersService = new ListenersService();
this.addListener = listenersService.addListener;
this.removeListener = listenersService.removeListener;
this.getData = function(){
return Data;
}
setInterval(function(){
Data.value++;
listenersService.notifyListeners(Data);
}, 100);
}
var dataSevice = new DataService(ListenersService);
var halloDataDynamic = function(callback){
var data = dataSevice.getData();
if(callback){
dataSevice.addListener(function(data){
callback(data);
});
}
return data;
};
var halloDataStatic = dataSevice.getData();
var World = React.createClass({
render: function() {
return <strong>{this.props.data.value}</strong>;
}
});
var Hello = React.createClass({
getInitialState: function() {
var data;
if(typeof this.props.halloData === 'function'){
data = this.props.halloData(this.updateHandler)
}
else data = this.props.halloData;
return {
data: data
};
},
updateHandler: function(data) {
this.setState({
data: data
});
},
render: function() {
return (
<div>
Value {this.props.name}: <World data={this.state.data} />
</div>
);
}
});
</script>
React.renderComponent(<Hello halloData={halloDataStatic} name="static"/>, document.getElementById('static'));
React.renderComponent(<Hello halloData={halloDataDynamic} name="dynamic"/>, document.getElementById('dynamic'));
Si vous transmettez les données comme accessoires à votre composant enfant, vous pouvez simplement les mettre à jour à un niveau supérieur et cela forcera un rendu à tous les composants qui utilisent le même objet de propriété. Considérez cet exemple simple:
var World = React.createClass({
render: function() {
return <strong>{this.props.name}</strong>;
}
});
var Hello = React.createClass({
clickHandler: function() {
this.setProps({ name: 'earth' });
},
render: function() {
return (
<div>
Hello <World name={this.props.name} />
<button onClick={this.clickHandler}>Click me</button>
</div>
);
}
});
Désormais, lorsque l'utilisateur clique sur le bouton, vous modifiez la propriété du composant Hello
, mais puisque vous avez transmis le même objet de propriété (ou de données) aux enfants, ils y réagiront et mettront à jour le DOM fantôme en conséquence.
Voici un violon de ce que je veux dire: http://jsfiddle.net/xkCKR/
Si vous avez un objet de données externe, vous pouvez simplement le transmettre au composant supérieur. N'oubliez pas que cela ne signifie pas qu'il existe une liaison bidirectionnelle:
// simple example of a data model
var Data = { name: 'world' };
var World = React.createClass({
render: function() {
return <strong>{this.props.data.name}</strong>;
}
});
var Hello = React.createClass({
clickHandler: function() {
this.setProps({
data: { name: 'earth' }
});
},
render: function() {
return (
<div>
Hello <World data={this.props.data} />
<button onClick={this.clickHandler}>Click me</button>
</div>
);
}
});
React.renderComponent(<Hello data={Data} />, document.body);
Cela fonctionne car react utilise une liaison unidirectionnelle des propriétés. Mais si, par exemple, votre composant enfant mettrait à jour ses propriétés, il ne grimperait pas jusqu'à son parent. Pour cela, vous aurez besoin du module complémentaire ReactLink ou utilisez une interface pub/sub comme celle fournie par Backbone.