web-dev-qa-db-fra.com

Modification d'une structure de données riche dans React.js

J'essaie de créer un simple éditeur basé sur une grille pour une structure de données et j'ai quelques problèmes conceptuels avec React.js. Leur documentation n'est pas très utile à ce sujet, donc j'espère que quelqu'un ici pourra vous aider.

Premièrement, quelle est la bonne façon de transférer l'état d'un composant externe à un composant interne? Est-il possible d'avoir des changements d'état dans le composant interne "bouillonnant" vers le (s) composant (s) externe (s)?

Deuxièmement, deux composants distincts peuvent-ils partager des données, de sorte qu'une mutation dans l'un est visible dans l'autre?

Voici un exemple simplifié du genre de chose que je veux faire ( version JSFiddle ):

J'ai un objet company contenant un tableau d'objets employee. Je souhaite disposer la liste employee dans une grille modifiable. Lorsque je clique sur le bouton, je veux voir l'objet company résultant, ainsi que toutes les mutations (écrit dans la console).

/** @jsx React.DOM */

var Cell = React.createClass({
    getInitialState: function () {
        return {data: ""};
    },

    componentWillMount: function () {
        this.setState({data: this.props.data});
    },
    onChange: function (evt) {
        console.log(this.state, evt.target.value);
        this.setState({data: evt.target.value});
    },
    render: function () {
        var data = this.props.data;
        return <input value={this.state.data} onChange={this.onChange} />
    }
});

var Row = React.createClass({
    render: function () {
        return (<div className="row">
            <Cell data={this.props.data.name} />
            <Cell data={this.props.data.location} />
            <Cell data={this.props.data.phone} />
        </div>);
    }
});

var Grid = React.createClass({
    render: function () {
        var rows = this.props.data.map(function (rowData, index) {
            return <Row key={index} data={rowData}>Row</Row>;
        });
        return <div className="table">{rows}</div>;
    }
});

var Button = React.createClass({
    getInitialState: function () {
        return {data: {}}
    },
    componentWillMount: function () {
        this.setState({data: this.props.data});
    },
    onClick: function () {
        console.log(this.state);
    },
    render: function () {
        return <button onClick={this.onClick}>Update</button>;
    }
});

var company = {
    name: "Innotech",
    employees: [
        {id: "1", name: "Peter", location: "IT", phone: "555-1212"},
        {id: "2", name: "Samir", location: "IT", phone: "555-1213"},
        {id: "3", name: "Milton", location: "loading dock", phone: "none"}
    ]
};


React.renderComponent(
    <div><Grid data={company.employees} /><Button data={company} /></div>,       
    document.getElementById('employees')
);
46
Andrew

Je pense que c'est la partie la moins documentée de React en ce moment. La façon suggérée de communiquer entre les composants est de simplement définir des accessoires lors de la communication du parent à l'enfant et de passer des rappels via les accessoires lors de la communication de l'enfant au parent.

Lorsque vous sentez que vous souhaitez partager des données entre des composants frères, cela signifie qu'il doit y avoir un composant parent gérant l'état et le transmettant aux deux composants. La plupart du temps, votre état doit vivre près du sommet de la hiérarchie de vos composants, et chaque élément d'information doit vivre dans (au plus) l'état d'un composant, pas plus.

Pour un peu plus à ce sujet, voir le blog de Pete Hunt, Thinking in React.


Dans cet esprit, j'ai mis à jour votre violon .

J'ai changé Grid pour qu'il ne conserve pas son propre état mais affiche toujours les données transmises via ses accessoires, et appelle onCellChange à partir de ses accessoires quand il veut request une modification des données de son parent. (Le composant Grid attendra de son parent qu'il mette à jour l'accessoire data de la grille avec les données modifiées. Si le parent refuse (peut-être en raison d'un échec de la validation des données ou similaire), vous vous retrouvez avec une lecture -seule grille.)

Vous remarquerez également que j'ai créé un nouveau composant Editor pour envelopper la grille et son bouton frère. Le composant Editor gère désormais essentiellement la page entière. Dans une véritable application, il est probable que le contenu de la grille soit nécessaire ailleurs et que l'état soit déplacé plus haut. J'ai supprimé votre composant Button, car il ne faisait pas grand-chose au-delà du composant natif <button> tag; J'ai quitté Cell mais il pourrait aussi être supprimé - Row pourrait facilement utiliser <input> balises directement.

J'espère que cela a du sens. N'hésitez pas à demander si quelque chose n'est pas clair. Il y a généralement aussi des gens dans la # reactjs IRC room si vous voulez discuter plus à ce sujet.

53
Sophie Alpert

J'explore ReactJS depuis une semaine environ. Ma contribution à votre question est de poser une nouvelle question: pourquoi séparez-vous le composant Cell des composants Row et Grid?

Issu d'un arrière-plan de Backbone, Cell & Row & Grid est logique, pour avoir un contrôle granulaire sur chaque Cell Backbone. Cependant, il semble que ce contrôle granulaire et la mise à jour DOM soient ce que ReactJS essaie de résoudre pour vous, ce qui me dit avoir un composant Grid qui implémente une ligne/cellule en lui-même:

var Grid = React.createClass({
    onChange: function(evt, field) {
        this.props.data[field] = evt.target.value;
    },

    render: function () {
        var rows = this.state.data.map(function (rowData, index) {
            return (
                <div className="row" key={index}>
                    <input value={rowData.name} onChange={this.onChange.bind(null, "name")} />
                    <input value={rowData.location} onChange={this.onChange.bind(null, "location")} />
                    <input value={rowData.phone} onChange={this.onChange.bind(null, "phone")} />
                </div>
            );
        });

        return <div className="table">
                   {rows}
               </div>;
    }
});

(Ignorez la gestion des * changements, il y a place à amélioration. Code non testé)

La question est, voudriez-vous jamais réutiliser Cell ou Row comme composants individuels ailleurs? Pour moi, la réponse est "très probablement non". Dans ce cas, ma solution ci-dessus est logique, à mon humble avis, et se débarrasse du problème de transmission de données et de modifications de haut en bas.

7
Bernardo

Une autre façon de partager des données entre des composants frères lorsqu'un composant parent n'a pas de sens consiste à utiliser des événements entre les composants. Par exemple, vous pouvez utiliser Backbone.Events, Node.js Emitter porté sur le navigateur ou n'importe quelle bibliothèque similaire. Vous pouvez même utiliser Bacon.js si vous préférez les flux réactifs. Il y a un excellent et simple exemple de combiner Bacon.js et React.js ici: http://joshbassett.info/2014/reactive-uis-with-react-and-bacon/

0
DjebbZ