Search code examples
reactjsreactjs-flux

How to tell different component when add to same listener in React


All:

I am pretty new to React and FLUX, when I tried to follow FLUX TodoMVC example and make a example, the problem is I need to know how to mkae the click on button group only update its color text, but not affect the other component's color text( right now, if I click either div1's or div2's, both colortexts will change, I just want to according text changes color.): I know the store design right now is not for two components, but let assume the store can maintain a color list for according components, the question is still "How can I know which component is clicked"

// app.js
var Dispatcher = new (require("./Dispatcher"));
var assign = require("object-assign");
var React = require("react");
var ReactDOM = require("react-dom");

var EventEmitter = require("events");



var TodoStore = assign({}, EventEmitter.prototype, {
    color: "black",
    dispatcherIndex: Dispatcher.register(function(payload){
        var type = payload.type;
        var data = payload.data;
        switch(type){
            case "Change_Color": {
                TodoStore.color = data.color;
                TodoStore.emitChange();
                break;
            }
        }
    }),
    getColor: function(){
        return this.color;
    },
    emitChange: function(){
        this.emit("CHANGE");
    },
    addChangeListener: function(callback){
        this.on("CHANGE", callback);
    },
    removeChangeListener: function(callback){
        this.removeListener("CHANGE", callback)
    }
});

var ColorButtonGroup = React.createClass({
    setColor: function(color){
        Dispatcher.dispatch({
            type:"Change_Color",
            data: {
                "color": color
            }
        });
    },
    render: function(){
        return (
            <div>
            <button style={{color:"red"}} onClick={this.setColor.bind(this,"red")}>RED</button>
            <button style={{color:"green"}} onClick={this.setColor.bind(this,"green")}>GREEN</button>
            <button style={{color:"blue"}} onClick={this.setColor.bind(this,"blue")}>BLUE</button>
            </div>
        );
    }
});

var ColorText = React.createClass({
    getInitialState: function(){
        return {
            style: {
                color: "black"
            }
        }
    },
    render: function(){
        return (
            <div style={this.state.style}>Color is: {this.state.style.color}</div>
        );
    },
    componentDidMount: function(){
        TodoStore.addChangeListener(this._onChange.bind(this));
    },
    componentWillUnmount: function(){
        TodoStore.removeChangeListener(this._onChange.bind(this));
    },
    getColor: function(){
        return TodoStore.getColor();
    },
    _onChange: function(){
        this.setState({
            style: {
                color:this.getColor()
            }
        });
    }
});

ReactDOM.render((<div>
    <ColorButtonGroup></ColorButtonGroup>
    <ColorText></ColorText>
    </div>), 
document.getElementById("div1"));
ReactDOM.render((<div>
    <ColorButtonGroup></ColorButtonGroup>
    <ColorText></ColorText>
    </div>), 
document.getElementById("div2"));

And the page:

// bundle is transpiled+browserify app.js and dependecies 
<html>
<head>
    <title>LEARN FLUX</title>
</head>
<body>
    <div id="div1"></div>
    <div id="div2"></div>
</body>
    <script src="bundle.js"></script>
</html>

Solution

  • Assuming you update your store to save multiple colors:

    var TodoStore = assign({}, EventEmitter.prototype, {
        colors: {},
        dispatcherIndex: Dispatcher.register(function(payload){
            var type = payload.type;
            var data = payload.data;
            switch(type){
                case "Change_Color": {
                    TodoStore.colors[data.colorKey] = data.color;
                    TodoStore.emitChange();
                    break;
                }
            }
        }),
        getColor: function(key){
            return this.colors[key];
        },
        // ...
    });
    

    You need some way to identify which piece of data to update in the store. You could, for example, pass a property:

    ReactDOM.render((<div>
        <ColorButtonGroup colorKey="1" />
        <ColorText colorKey="1" />
        </div>), 
    document.getElementById("div1"));
    ReactDOM.render((<div>
        <ColorButtonGroup colorKey="2" />
        <ColorText colorKey="2" />
        </div>), 
    document.getElementById("div2"));
    

    Then, when you dispatch the action, you pass along the key so the store knows what data to update:

    setColor: function(color){
        Dispatcher.dispatch({
            type:"Change_Color",
            data: {
                colorKey: this.props.colorKey,
                "color": color
            }
        });
    },
    

    Similarly, you'd look up the data in the appropriate store in ColorText:

    getColor: function(){
        return TodoStore.getColor(this.props.colorKey);
    },