Robson Robson - 2 months ago 7
Javascript Question

How to pass value from included component's state back to parent component?

I have a problem to find a way to easy get state value from component in other component. When I click on any tag.

I want to append value of tag to list state in Form component if tag is clicked. Is there any simply way to do this ?

I have component like this:

var Tag = React.createClass({
getInitialState: function(){
return {
checked: false
};
},
componentDidMount: function() {
this.setState({
checked: false
});
},
_onChange: function(event) {
if(this.state.checked == false){
this.setState({
checked: true
});
} else {
this.setState({
checked: false
});
}
},
render: function() {
return (
<div className="review-tag">
<input
type="checkbox"
id={this.props.id}
name={this.props.name}
checked={this.state.checked}
value={this.props.id}/>
<label htmlFor={this.props.name} onClick={this._onChange}>{this.props.name}</label>
</div>
);
return (
<div>
<span>{this.props.name}</span>
</div>
);
}
});

var allTags = tags;

var ReviewTag = React.createClass({
render: function() {
const tagComps = allTags.map(function(tag){
return <Tag {...tag}/>;
});
return (
<div>
{tagComps}
</div>
);
}
});


Tag component have included value at rendering and I have to get value of each tag in my ReviewForm component which looks like this:

var fd = new FormData();
var ReviewForm = React.createClass({
getInitialState: function(){
return {
Author: '',
Tags: ''
};
},

componentDidMount: function() {
this.setState({
Author: author,
Tags: tags
});
},

submit: function (e){
var self;

e.preventDefault();
self = this;

var data = {
tags: this.state.Tags,
author: this.state.Author
};

for (var key in data) {
fd.append(key, data[key]);
}
$.ajax({
type: 'POST',
url: '/reviews/submit/',
data: fd,
processData: false,
contentType: false
})
.done(function(data) {
console.log('Review added successfully.');
})
.error(function(msg) {
var errors = msg.responseJSON;
console.log(errors);
});
},
render: function() {
<div className="scolumn">
<ReviewTag/>
</div>
}
})


Many thanks for any help.

Answer
  1. You don't need to set state in componentDidMount — remove that piece of code.

  2. Really, couldn't all of that...

_onChange: function(event) { if(this.state.checked == false){ this.setState({ checked: true }); } else { this.setState({ checked: false }); } },

be substituted with _onChange: function(event) { this.setState(checked: !this.state.cheked) }?

  1. allTags should be passed as property, not as outer scope variable.

Now to your main question.

Move state to parent component (ReviewForm). You can have state as a hash with {name: isChecked} structure (name is Tag's name, and isChecked is boolean). Pass it down to ReviewTag and futher down to Tag as property. Also pass down your _onChange handler as property (it should be moved to parent component — ReviewForm — as well).

So when the Tag is checked, it calls ReviewForm's handler, and ReviewForm's handler changes its own state accordingly.