I'm rewriting the UI for my web-app in react.js, and I'm a bit stumped by the following problem.
I have a page which displays data obtained via an AJAX request, and below that, a form to submit new data is displayed. All good.
Now, I want to add a <select>
element to the form, and fetch the values from a different location (url).
The current code (without the <select>
) looks like this (simplified a bit, but all the working details are the same; it mostly follows the tutorial on the react.js website):
var tasks_link = $('#tasks_link');
var getDataMixin = {
loadDataFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadDataFromServer();
}
};
var sendDataMixin = {
handleDataSubmit: function(senddata) {
$.ajax({
url: this.props.url,
dataType: 'json',
contentType: 'application/json',
type: 'POST',
data: senddata,
success: function(data) {
var curr_d = this.state.data;
var curr_d_new = curr_d.concat([data]);
this.setState({data: curr_d_new});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
}
};
var taskForm = React.createClass({
handleSubmit: function() {
var name = this.refs.task_name.getDOMNode().value.trim();
if (!name) {
return false;
}
this.props.onTaskSubmit(JSON.stringify({name: name}));
this.refs.task_name.getDOMNode().value = '';
return false;
},
render: function () {
return (
<form className="well base_well new_task_well" onSubmit={this.handleSubmit}>
<div className="form-group">
<div className="input-group">
<span className="input-group-addon no_radius">Task name</span>
<input type="text" className="form-control no_radius" id="add_new_project_input" ref="task_name"/>
</div>
</div>
<button type="button" className="btn btn-default no_radius add_button" id="add_new_task_btn" type="submit">Add task</button>
</form>
);
}
});
var taskBox = React.createClass({
mixins: [getDataMixin, sendDataMixin],
render: function () {
return (
<div id="project_box" className="taskBox"> <taskList data={this.state.data} />
<taskForm onTaskSubmit={this.handleDataSubmit}/> </div>
);
}
});
tasks_link.click(function() {
React.renderComponent(
<taskBox url="/api/tasks/" />,
document.getElementById('content_container')
);
});
Now, I can add a select
element by adding a getDataMixin
to TaskForm
, fetching the data and building a list of possible options, but I will need to have forms with many lists, and the approach doesn't seem to scale (due to naming collisions; or I'll need to use something other than mixins).
So I though of creating a separate React class
, which would just have the getDataMixin
, receive the API url via the parent setting its props
, and render the <select>
element; and use this class inside the form.
But I have no idea how to access the selected value (since a parent cannot access it's child's refs
).
So I need another way to pass the selected value "up".
Or, in case this isn't possible, a nudge in the correct direction – I don't want to end up with a ton of un-reusable code (part of the reason why I switched to react was to use mixins and keep the code to a sane and readable minimum).