web-dev-qa-db-fra.com

Transmettez les nouvelles données du serveur aux composants react.js

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:

  1. Exécutez à nouveau renderComponent avec de nouvelles données et utilisez les techniques de réaction diff DOM. Cela m'inquiète, car je devrai le faire pour tout petit changement de données.
  2. Appelez setState pour les composants qui contiennent ces modèles. De cette façon, les données ne deviennent pas des accessoires, mais indiquent ce qui (d'après ce que je comprends) n'est pas une bonne pratique. De plus, je ne vois aucun moyen d'obtenir référence au composant enfant en dehors du composant racine.
  3. Ayant plusieurs appels de renderComponent, de cette façon, j'aurai accès à setProps pour n'importe lequel de ces composants. Mais alors je devrai faire un travail de création de modèles (pour avoir tous les conteneurs disponibles sur la page) et ça tue toute l'idée de réagir.
  4. Avoir un composant racine qui inclut tous les modèles possibles dans l'application affiché aux utilisateurs et appeler setProps pour changer de modèle. Ma préoccupation ici, que cette composante devienne assez grande et devienne "spaghetti" à un moment donné + les préoccupations du point 1.

Merci d'avance et j'espère avoir pu expliquer clairement mon problème.

23
Sergey Shvets

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.

6
krs

Pour le moment, je connais au moins trois façons de transmettre de nouvelles données à un composant:

  1. Composer de nouveau rendu. Ne vous inquiétez pas de l'efficacité de cette méthode car React semble gérer cela très bien. Il y a de bons articles à ce sujet: Changement et sa détection dans les cadres JavaScript et - Mise à jour avec React.render
  2. Utilisez PubSub pour permettre au composant d'être notifié du changement de données (quelques exemples utiles que vous pouvez trouver dans le Comment communiquer entre React composants post)).
  3. Liaison avec un rappel (voir trois jsfiddles ci-dessous)

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'));
5
Bulki S Maslom

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.

5
David Hellsing