9

I'm really new to React, and I'm pulling my hair out trying to solve what seems to me to be a simple problem. Here's a picture of the component I've built.

Color Picking Component

What I'm trying to accomplish seems trivial, but literally every article I've read explaining what to do has told me something different, and not one of the solutions has worked. It breaks down to this: When a user clicks on a tag, it builds out a tray and loops through an array of colors to build color buttons. When a color button is clicked it needs to pass which color was clicked to its parent component and run a function to update its color. I've read about flux, event bubbling, binding "this" to a property, and none of those solutions has seemed to work. The React docs are basically useless for a newbie like myself. I want to avoid complicated event structures like flux at this point since I'm appending some simple components to an existing app that I didn't write in React originally.

Also, PS, This code is in JSX which makes much more sense to me than straight JS react. Thanks in advance for your help!

var colorsArray = ["#ED5851", "#9CCC64", "#337AEC", "#ff7a45", "#7E58C2", "#FFEB3B", "#78909C", "#FFFFFF", "#213a4b"];

var EditDrawer = React.createClass({
    updateColor: function() {
        console.log("New Color: " + i);
    },
    render: function(){
        var passTarget = this;
        return (
            <div className="container-fluid animated fadeIn extra-fast-animation tag-edit-drawer">
                <div className="row">
                    <div className="col-xs-12">
                        {colorsArray.map(function(object, i){
                            return <ColorButton buttonColor={object} key={i} />;
                        })}
                    </div>
                </div>
            </div>
        );
    }
})

var ColorButton = React.createClass({
    render: function(){
        var buttonStyle = {
            backgroundColor: this.props.buttonColor,
        };
        return (
            <div className="tag-edit-color-button" style={buttonStyle} >
            </div>
        )
    }
})
Chris Patty
  • 189
  • 1
  • 3
  • 12
  • 1
    As a note, here's two techniques I tried to use but neither was working. http://haochuan.io/how-to-communicate-between-pure-react-components-without-flux/ http://andrewhfarmer.com/component-communication/#3-callback-functions – Chris Patty May 20 '16 at 23:40

3 Answers3

9

The callback function should work. As you've mentioned in your previous comment you can use your captured this to access the updateColor function from the child:

var passTarget = this;
...
...
return <ColorButton 
  buttonColor={object} 
  key={i} 
  update={passTarget.updateColor} />

Or as Bogdan mentioned you can pass it through map after your callback function:

{colorsArray.map(function(object, i){
  return <ColorButton 
           buttonColor={object} 
           key={i} 
           update={this.updateColor} />;
}, this)}

Your <ColorButton /> component should then be able to run a simple onClick function:

onClick={this.props.update}

And then you can simply make use of normal event targets in the parent component to capture the color of the button that was clicked:

updateColor: function(e) {
  console.log(e.target.style.backgroundColor);
}

Here is a full DEMO to demonstrate.

Brad Colthurst
  • 2,515
  • 14
  • 13
3

You can just pass callback function into child from your parent component, as simple as this:

 <ColorButton buttonColor={object} key={i} onColorSelect={this.updateColor}/>

(when using React.createClass all class methods are already bound to this, so you are not required to call .bind(this)).

Then from ColorButton component you can call this callback as this.props.onColorSelect(...).

JS Bin example: http://jsbin.com/fivesorume/edit?js,output

Bogdan Savluk
  • 6,274
  • 1
  • 30
  • 36
  • Thanks Bogdan! Unfortunately this just returns the error: "Cannot read property 'updateColor' of undefined". I think it's because I'm looping through an array and so "this" is being overridden. To get around that I've defined a variable earlier to capture "this" and this allows me to access the updateColor function from the child. The only problem is that if I try to pass anything to the function it will run the function nine times on creation instead of waiting for a click. `onClick={this.props.onColorSelect(this.props.buttonColor)}` [outputs this](http://imgur.com/hTFcUN3) – Chris Patty May 21 '16 at 00:00
  • about passing `this` into Array.map - you can also pass it as the second argument `.map(function..., this)`, then this from outer scope would be used. About `onClick={this.props.onColorSelect(this.props.buttonColor)}`, you are not passing function here - you are calling it during rendering and passing the result. You need to create separate function in your component. – Bogdan Savluk May 21 '16 at 00:13
  • This makes a lot of sense. I'll give it a try next week. Thanks for your help! – Chris Patty May 21 '16 at 01:35
1

OK, this is pretty simple in React without using flux or redux, similar to passing down props from parent to child, here we can use callback function like this:

var colorsArray = ["#ED5851", "#9CCC64", "#337AEC", "#ff7a45", "#7E58C2", "#FFEB3B", "#78909C", "#FFFFFF", "#213a4b"];


var EditDrawer = React.createClass({
    updateColor: function(i) {
        alert("New Color: " + i);
    },
    render: function(){
        return (
            <div className="container-fluid animated fadeIn extra-fast-animation tag-edit-drawer">
                <div className="row">
                    <div className="col-xs-12">
                        {colorsArray.map(function(object, i){
                            return <ColorButton buttonColor={object} key={i} updateColor={this.updateColor}/>;
                        }, this)}
                    </div>
                </div>
            </div>
        );
    }
});


var ColorButton = React.createClass({
    updateColor: function() {
        this.props.updateColor(this.props.buttonColor);
    },
    render: function(){
        var buttonStyle = {
            backgroundColor: this.props.buttonColor,
        };
        return (
            <div className="tag-edit-color-button" 
            style={buttonStyle}
            onClick={this.updateColor}>
              this.props.buttonColor
            </div>
        )
    }
});
Alireza
  • 100,211
  • 27
  • 269
  • 172