Child to parent communication in React (JSX) without flux - javascript

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>
)
}
})

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.

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

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>
)
}
});

Related

Is my owner react components state preventing me from updating child component state?

I'm trying to update the state of a checkbox within a modal that is mounted via button on the UI. I'm loading the settings when AppWrapper mounts so I can pass them around as needed. Right now i'm just passing the settings as props to SettingsList component, which then renders a series of child nodes as checkboxes. I'm able to click the checkboxes when the modal is open, and the settings successfully save to the database. However when the modal is closed and reopened the settings are refreshed to the initially set state from the owner. Refreshing the page though shows the accurately checked boxes. That makes sense to me, but i'm unsure they best way to resolve it.
Should I/Can I update the state of the parent from the child setting so when the modal is reopened that passed props reflect the user changes?
My react structure looks like this:
<AppWrapper>
getInitialState {settings:[]}
<Modal>
<SettingList settings={this.state.settings}>
<Setting/>
<SettingList/>
<Modal/>
<AppWrapper/>
It's not direct one to one code, bust just a representation of the hierarchy.
My Modal component looks like this:
var Modal = React.createClass({
render: function() {
if(this.props.isOpen){
return (
<ReactCSSTransitionGroup transitionName={this.props.transitionName} transitionEnterTimeout={500} transitionLeaveTimeout={500}>
<div className="mymodal">
{this.props.children}
</div>
</ReactCSSTransitionGroup>
);
} else {
return <ReactCSSTransitionGroup transitionName={this.props.transitionName} transitionName={this.props.transitionName} transitionEnterTimeout={500} transitionLeaveTimeout={500} />;
}
}
});
My SettingList component looks like this:
var SettingsList = React.createClass({
render: function() {
var settingNodes = this.props.settings.map(function(setting, i){
return (
<Setting data={setting}
key={i}>
</Setting>
)
}.bind(this));
return (
<div className="settings-block">
<h2>Notifications</h2>
<ul className="account-settings">
{settingNodes}
</ul>
</div>
)
}
});
And the Setting component looks like this:
var Setting = React.createClass({
saveSetting: function(one) {
core.setAccountSettings(this.refs.setting_checkbox.id, this.refs.setting_checkbox.checked).done(function(response){
this.setState({
defaultChecked: this.refs.setting_checkbox.checked
};
console.log(response)
}.bind(this));
},
render: function() {
//get values from settings object
for (var k in this.props.data) {
this.settingId = k
this.settingName = String(k.split(/_(.+)?/)[1]).replace(/_/g, " ");
this.settingValue = (this.props.data[k].toLowerCase() == "true")
}
return (
<li className="checkbox">
<input onChange={this.saveSetting} ref="setting_checkbox" id={this.settingId} className="settings_checkbox" type="checkbox" defaultChecked={this.settingValue}></input>
<label htmlFor={this.settingName}>{this.settingName}</label>
</li>
)
}
});
As pointed out in the comments above there is a number of ways to pass data between components.
http://andrewhfarmer.com/component-communication/
Following the article regarding callbacks was the solution for me.

React: Inject common render logic from parent

Let's assume i have a react component class that displays a modal dialog on a click of a button.
it can be created like this (in jsx):
<Modal text={"some text"}/>
Now, I have a bunch of component classes (let's call them Panels) that all have a function called getMessage, and i'd like the same behavior in all of these components: the modal dialog should show the string that returns from the call to getMessage.
the straight forward way to do this would be to include
<Modal text={this.getMessage()}/>
in the render() function for each such component.
Now, let's say that there is a bit more logic involved. for example, i would only like to render this component if getMessage is defined and does not return null.
Now this is starting to look like this:
var Panel1 = React.createClass({
getMessage: function() {return 'wow';},
render: function() {
var modal = null;
if (this.hasOwnProperty('getMessage' && this.getMessage() !== null) {
modal = <Modal text={this.getMessage()}/>
}
return (
<div>
{modal}
...all other stuff done in panel
</div>
);
}
});
This is starting to become cumbersome because I need to have this logic for each and every component class I define.
How can I achieve DRYness in this scenario so that i don't have to repeat this?
One way would be to define a utility function that contains this logic, let's call it displayModalIfNeeded and the call it from render. this now looks like this:
return (
<div>
{displayModalIfNeeded.call(this)}
....all other stuff needed in Panel
</div>
);
And now for my actual question (sorry for the long exposition):
Let's say that i have a parent component called <Dashboard> which has all panels as its childern:
<Dashboard>
<Panel1>
<Panel2>
<Panel3>
</Dashboard>
Is there something i can write in the implementation of Dashboard that will entirely remove the need to specify anything about these modal components in each Panel?
meaning the the Panel1 implementation can now just be
<div>
...all other stuff done in panel
</div>
and when it's rendered as a child of Dashboard it will have that modal dialog and accompanying logic.
I suggest using a wrapper component with the children prop. Your parent component would look like this:
<Dashboard>
<ModalWrapper text={msg1}>
<Panel1 />
</ModalWrapper>
<ModalWrapper text={msg2}>
<Panel2 />
</ModalWrapper>
<ModalWrapper text={msg3}>
<Panel3 />
</ModalWrapper>
</Dashboard>
Now all your conditional logic can be placed in ModalWrapper. Where your question has "....all other stuff needed in Panel", use this.props.children. e.g.
var ModalWrapper = React.createClass({
render: function () {
var text = this.props.text;
return (
<div>
{text ? <Modal text={text} /> : null}
{this.props.children}
</div>
);
}
});

React components won't render; only seeing data-reactid=".0"

I'm creating my first React app, so apologies in advance. A newbie must learn as he goes.
But I'm a few hours into debugging, having gotten nowhere, and I'm hoping someone can clarify why this attempt to pass data into a React component, use prototype.map, and render a final component just isn't cutting it.
var imagedata = [{"id":"1"},{"id":"2"},{"id":"3"}];
var portraitPhoto = React.createClass({
render: function() {
return (
<div className="test">
<img src={"./build/assets/images/photos/square_raw/" + this.props.imagepath + ".jpg"}
className="full-width-portrait" />
</div>
);
}
});
var portrait = React.createClass({
getDefaultProps: function(){
return {
data: imagedata
}
},
render: function() {
var portraitEach = this.props.data.map(function (imaged,i) {
return (
<div className="portrait2">
<portraitPhoto imagepath={imaged.id}/>
</div>
);
});
return (
<div className="portrait-container">
{portraitEach}
</div>
);
}
});
React.render(
<portrait/>,
document.getElementById('portraits')
);
You need your React components to have uppercase names. React's help page says:
React's JSX uses the upper vs. lower case convention to distinguish
between local component classes and HTML tags
Simply having this should be enough:
var PortraitPhoto = React.createClass({
...
...
});
A demo on jsfiddle is here.

Owl Carousel with React

I want to use Owl Carousel with React, and I am new to React.
Please see this jsfiddle, I spent much time to do it.
The JSX code
var Carousel = React.createClass({
componentDidMount: function(){
$(React.findDOMNode(this)).owlCarousel({
items: 4
});
},
render: function(){
return(
<div id="inx-carousel-thumb" className="owl-carousel">
{
this.props.images.map(function(image, index){
return (
<div className="item" key={index}><img src={image} /></div>
)
}.bind(this))
}
</div>
);
}
});
var imagesList = [['http://www.myfacewhen.net/uploads/3908-troll-smile.jpg', 'http://www.captionite.com/templates/thumb-success.jpg', 'http://pauljadam.com/forma11y/img/rage-guy-teeth-smile.jpg'],['http://i2.kym-cdn.com/entries/icons/original/000/004/073/smile.png']];
var Container = React.createClass({
getInitialState: function(){
return {
images: imagesList[0]
};
},
changeImages: function(index) {
this.setState({images: imagesList[index]});
},
render: function(){
return(
<div>
<Carousel images={this.state.images} />
<input type="button" onClick={this.changeImages.bind(this, 0)} value="Images List 0" />
<input type="button" onClick={this.changeImages.bind(this, 1)} value="Images List 1" />
</div>
);
}
});
$(document).ready(function(){
React.render(
<Container />,
document.getElementById('container')
);
});
I included Jquery, Reactjs, Owl Carousel, too.
The problem is that when I tap button "Images List 1" to change the source array of Owl Carousel, the item count of carousel should become 1. However, it is still 3, and it only change first item. I think React only replace first item and ignore whole div, but I don't know how to fix it. Please give me some hints.
You have two options here:
You should deinitialize (destroy) OwnCarousel on componentWillUpdate and init it againt on componentDidUpdate after React rerendered the component with new images
You should return false in shouldComponentUpdate and manually update DOM in componentWillReceiveProps using nextProps.images value and jQuery
sorry my bad english!
You should verify length of element the carousel will use.
Example: in this case, my element is .banner-img - refer elements of <img />
componentDidUpdate: function () {
if ($('.banner-img').length > 0) {
$("#owl-home-slider").owlCarousel();
}
}

Class names for dynamic children React

I have a set of buttons created from an array, however I'm unsure how to set individual classNames for them. Is there an easy way to this?
var ButtonContainer = React.createClass({
render: function(){
var answerList = this.props.answerList.map(function(input, i){
return <SingleButton key={'button'+i} singleAnswer={input}/>
}.bind(this));
return <div> {answerList} </div>
}
})
var SingleButton = React.createClass({
render: function(){
return (
<div>
<button className='quiz-button'>{this.props.singleAnswer}</button>
</div>
)
}
});
I've tried className={this.props.key} but that doesn't seem to work. Thanks for any help!
Since React v0.12 key and ref are removed from props:
You can no longer access this.props.ref and this.props.key from inside
the Component instance itself. So you need to use a different name for
those props.
That is why setting className={this.props.key} wont work. But you can try this:
return <SingleButton key={'button'+i} className={'button'+i} singleAnswer={input}/>
and then
<button className={this.props.className}>{this.props.singleAnswer}</button>
Related question: This.key in React.js 0.12

Categories