nikotromus nikotromus - 3 months ago 7
React JSX Question

reactjs cant modify a bound value

I am using reactjs with a parent and a child component. In the child, I am binding the value of a textarea to a property sent in by the parent. When I remove the child component by clicking the 'remove' button, the value of the textarea matches the the inner html text on the div of the child component, as expected. However, because the textarea's value is bound to the reactjs property, I am unable to modify the contents of the text area.

I want to be able to change the value of the text area, save, and still have the child components delete correctly. I'm sure it's something simple, but I can't seem to figure it out...

//Child
var Comment = React.createClass({
remove: function () {
this.props.deleteFromBoard(this.props.index)
},
save: function () {
var val = this.refs.newText.value;
console.log('New Comment: ' + val);
this.props.updateCommentText(val, this.props.index);
this.setState({editing:false})
},
render: function () {
return (
<div className="commentContainer">
<div className="commentText">{this.props.myVal}</div>
<textarea ref="newText"
value={this.props.myVal}
onChange={this.handleChange}></textarea>
<button onClick={this.save} className="button-success">save</button>
<button onClick={this.remove} className="button-danger">Remove Question</button>
</div>
);
}
});

//Parent
var Board = React.createClass({
//Initial state is an array with three different strings of text 'comments'
getInitialState: function(){
return {
comments: [
'One',
'Two',
'Three',
'Four'
]
}
},
removeComment: function(i){
console.log('Removing comment: ' + i + ' bkbk');
var arr = this.state.comments;
//Spicing the array (where do you want to start? 'i', how many do you want to remove? '1'
arr.splice(i,1);
this.setState({comments:arr})
},
reportMe: function(){
var arr = this.state.comments;
var arrayLength = arr.length;
for (var i = 0; i < arrayLength; i++) {
alert(arr[i]);
}
},
updateComment: function(newText, i){
var arr = this.state.comments;
arr[i] = newText
this.setState({comments:arr})
},
//Properties of each comment
eachComment: function(text, i) {
return (
<Comment
key={i} index={i} myVal={text} updateCommentText={this.updateComment} deleteFromBoard={this.removeComment}></Comment>

);
},
render: function(){
return (
<div>
<button onClick={this.reportMe.bind(null)} className="button-success">Report Contents Of Array</button>
<div className="board">
{
this.state.comments.map(this.eachComment)
}
</div>
</div>
)
}
})
ReactDOM.render(<Board/>, document.getElementById('container'));

Answer

Try this :

var Comment = React.createClass({
    getInitialState: function(){
     return {
        myVal : this.props.myVal || '',
     }
    },
    componentWillReceiveProps: function(nextProps){
         if(this.state.myVal !== nextProps.myVal){
             this.setState({myVal : nextProps.myVal});
          }
    },
    handleChange : function(event){ 
        this.setState({
            myVal : event.target.value,
        });
        ...
    },
    ....
    save: function () {
        var val = this.state.myVal;
        console.log('New Comment: ' + val);
        this.props.updateCommentText(val, this.props.index);
        this.setState({editing:false})
    },
     render: function(){
       (
           <div className="commentContainer">
               <div className="commentText">{this.props.myVal}</div>
               <textarea ref="newText"
                         value={this.state.myVal} <-- state.myVal instead of props.myVal
                         onChange={this.handleChange.bind(this)}>
               </textarea>
               <button onClick={this.save.bind(this)} className="button-success">save</button>
               <button onClick={this.remove.bind(this)} className="button-danger">Remove Question</button>
           </div>
       );
     }

   }
});