I'm trying to wrap my head around the best way to update data through an HTML form. Take the example from the ReactJS webpage:
https://facebook.github.io/react/docs/tutorial.html
Say you wanted to implement the ability for a user to edit a comment that she had posted in the past (i.e. not in the current session and so must be fetched from the server). The edit comment page would need to prefill the comment's existing author name and text. How would you implement fetching the comment data and pre-filling the comment form? Here are the conflicting ideas I have in my head that I can't sort out (let's call the new component CommentEdit
):
CommentEdit
since they are not stateCommentEdit
component should be reusable so it should be able to fetch the initial data from the server itself, but then it has to be saved as stateCommentEdit
were to fetch and set the props for CommentEdit
, it would have to save it as state, so there aren't much savings thereHonestly, I just wish components could change their own props. Seems like it would make components a lot more reusable.
I ended up solving this by adding a "wrapper" parent to each of my components. The wrapper component is responsible for fetching data from the server, which it will store in its state. The wrapper will pass the fetched state as props to its children. I then abstracted this setup as mixins, creating a wrapper mixin and wrapper child mixin so that I can re-use this setup easily.
This pattern works really well for typical forms in your app. The wrapper will fetch data to pre-populate the form and the child will send the form to the server and will be responsible for error-handling.
Here's an example:
// This handles the form submission and error handling
var EditFormMixin = {
getInitialState: function() {
return {formState:FORM_STATE_EDITING, errors:{}}
},
handleSubmitBase: function(event, url, data, method, success, error) {
event.preventDefault();
this.setState({formState:FORM_STATE_SUBMITTING, errors:{}});
$.ajax({
url: url,
dataType: "json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
method: method,
success: function(data) {
this.setState({formState:FORM_STATE_SUBMITTED});
if (success) {
success();
};
}.bind(this),
error: function(xhr, status, err) {
this.setState({formState:FORM_STATE_EDITING});
if (xhr.status == 400) {
this.setState({errors:xhr.responseJSON});
}
if (error) {
error();
}
}.bind(this)
});
}
};
// This handles fetching the initial data for the form
var EditFormWrapperMixin = {
getInitialState: function() {
return {data: {}, loadState:LOAD_STATE_UNLOADED, url:this.getUrl()};
},
fetch: function() {
this.setState({loadState:LOAD_STATE_LOADING})
$.ajax({
url: this.state.url,
dataType: "json",
method: "GET",
success: function(data) {
this.setState({data:data, loadState:LOAD_STATE_LOADED});
}.bind(this),
error: function(xhr, status, err) {
}.bind(this)
});
}
componentDidMount: function() {
this.fetch();
},
componentWillReceiveProps: function(nextProps) {
var url = this.getUrl();
this.setState({url:url}, function() {
this.fetch();
});
}
};