Summary:
I'm attempting to make a blog with React and seem to have hit a snag in setting and updating a text field. I have the blog text and user name from a database and want to set the blog text in a text window for a given user. Then when the user types I also want to update that text.
Attempt:
If I set my textarea value={this.props.name}
it will correctly set the value but won't update. If I set it to update with {this.state.value}
then it starts off blank but will update correctly. I can find examples of how either set the default value or update the value. I can't figure out how to do both. Thank you in advance.
ClickScreen React Component Call within App
<ClickScreen
setClicks={this.setClicks}
setUsername={this.setUsername}
setBlog={this.setBlog}
onLogout={this.onLogout}
counter={this.state.counter}
blogtext={this.state.blogtext}
username={this.state.username}
/>
BlogEntry React Component Call within ClickScreen Page:
<EditBlog name={this.props.blogtext} username={this.props.username} ></EditBlog>
EditBlog React Component
// Allows a user to edit a blog entry.
var EditBlog = React.createClass({
displayName: 'Editor',
propTypes: {
name: React.PropTypes.string.isRequired
},
getInitialState: function() {
console.log("getInitialState value: " + this.props.name);
return {
value: this.props.name,
};
},
componentDidMount: function() {
this.setState({value: this.props.name});
},
handleChange: function(event) {
console.log("changing the text area to: " + event.target.value)
this.setState({value: event.target.value});
},
updateBlogText: function() {
ajax('update_blog.php', { blogtext: this.value, username: this.props.username }, function(response) {
console.log("Updating blog text to: " + this.state.value + " with user: " + this.props.username);
if (response.result === 'success') {
console.log("Success!");
//this.props.setClicks(response.counter, response.username, response.blogtext);
//this.props.setUsername(response.username);
//this.props.setBlog(response.blogtext);
}
else if (response.result === 'error') {
alert('Error: ' + response.msg);
console.log("Error!");
}
else {
alert('Response message has no result attribute.');
}
}.bind(this));
console.log("Click button clicked");
},
render: function() {
console.log("this.state.value: " + this.state.value)
console.log("this.state.value blogtext: " + this.props.name);
this.state.value = this.props.value;
return (
<div>
<h2>Blog Entry</h2>
<center>
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" onChange={this.handleChange} defaultValue={this.props.name}></textarea>
<input type="submit" value="Save" onClick={this.updateBlogText}/>
</form>
</center>
</div>
);
}
});
Update: There were some issues in my understanding between the distinction of a controlled and uncontrolled React component and the React lifecycle. Now the value is set in getInitialState and then updated appropriately in componentWillReceiveProps. Thank you everyone!
Changes:
defaultValue={this.props.name}
has been changed to reflect the updated state value of value={this.state.value}
.this.state.value=this.props.value
in the render function has been removed. // Allows a user to edit a blog entry.
var EditBlog = React.createClass({
displayName: 'Editor',
propTypes: {
name: React.PropTypes.string.isRequired
},
getInitialState: function() {
console.log("getInitialState value: " + this.props.name);
return {
value: this.props.name,
};
},
componentWillReceiveProps: function(nextProps) {
console.log("componentWillReceiveProps: " + nextProps.name);
this.setState({value: nextProps.name});
},
handleChange: function(event) {
console.log("changing the text area to: " + event.target.value);
this.setState({value: event.target.value});
},
updateBlogText: function() {
ajax('update_blog.php', { blogtext: this.value, username: this.props.username }, function(response) {
console.log("Updating blog text to: " + this.state.value + " with user: " + this.props.username);
if (response.result === 'success') {
console.log("Success!");
//this.props.setClicks(response.counter, response.username, response.blogtext);
//this.props.setUsername(response.username);
//this.props.setBlog(response.blogtext);
}
else if (response.result === 'error') {
alert('Error: ' + response.msg);
console.log("Error!");
}
else {
alert('Response message has no result attribute.');
}
}.bind(this));
console.log("Click button clicked");
},
render: function() {
return (
<div>
<h2>Blog Entry</h2>
<center>
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" onChange={this.handleChange} value={this.state.value}></textarea>
<input type="submit" value="Save" onClick={this.updateBlogText}/>
</form>
</center>
</div>
);
}
});
To get the value of an input field in React:Use event. target. value to get the input field's value and update the state variable.
A component cannot update its own props unless they are arrays or objects (having a component update its own props even if possible is an anti-pattern), but can update its state and the props of its children.
The defaultProps is a React component property that allows you to set default values for the props argument. If the prop property is passed, it will be changed. The defaultProps can be defined as a property on the component class itself, to set the default props for the class.
so, whenever you want to edit props, you have to set them in the local component state ( like you did in the getInitialState by setting value to the props ). Props are immutable, so setting the value or defaultValue of the input to the prop will/should not allow you to change the prop.
So below, what I have done is set value of the input to this.state.name, and this.state.name is set to this.props.name on initial state and whenever you call update, it will update the components state with whatever is typed in the input.
if you need to pass the name back to a parent component, then you need to pass in a function from the parent which you can then call from the child to inform the parent that the name value has changed, parent will then need to update the name which then gets passed back down to the child...this creates a 2 way data binding..
// Allows a user to edit a blog entry.
var EditBlog = React.createClass({
displayName: 'Editor',
propTypes: {
name: React.PropTypes.string.isRequired
},
getInitialState: function() {
console.log("getInitialState value: " + this.props.name);
return {
value: this.props.name,
};
},
componentWillReceiveProps: function ( newProps ) {
this.setState( { value:newProps.name } );
}
handleChange: function(event) {
console.log("changing the text area to: " + event.target.value)
this.setState({value: event.target.value});
},
updateBlogText: function() {
ajax('update_blog.php', { blogtext: this.value, username: this.props.username }, function(response) {
console.log("Updating blog text to: " + this.state.value + " with user: " + this.props.username);
if (response.result === 'success') {
console.log("Success!");
//this.props.setClicks(response.counter, response.username, response.blogtext);
//this.props.setUsername(response.username);
//this.props.setBlog(response.blogtext);
}
else if (response.result === 'error') {
alert('Error: ' + response.msg);
console.log("Error!");
}
else {
alert('Response message has no result attribute.');
}
}.bind(this));
console.log("Click button clicked");
},
render: function() {
console.log("this.state.value: " + this.state.value)
console.log("this.state.value blogtext: " + this.props.name);
this.state.value = this.props.value;
return (
<div>
<h2>Blog Entry</h2>
<center>
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" onChange={this.handleChange} value={value}></textarea>
<input type="submit" value="Save" onClick={this.updateBlogText}/>
</form>
</center>
</div>
);
}
});
For forms in React:
The defaultValue and defaultChecked props are only used during initial render. If you need to update the value in a subsequent render, you will need to use a controlled component.
Docs on controlled components
Reading your code, there is no need to:
componentDidMount: function() {
this.setState({value: this.props.name});
},
The job is already done by getInitialState
. If you intend to have the state updated when the component receive new props you should write:
componentWillReceiveProps: function(nextProps) {
this.setState({value: nextProps.name});
},
I think that, in your case, you should use this.state.value
and that it is not well initialized because componentWillReceiveProps
was no implemented to update the component state when receiving a props.name
value different from undefined
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With