I've been tinkering with converting one page in my web application to use react rather than jQuery and Vanilla JS but I'm getting stuck on how to approach it because of how unfamiliar I am with react.
At the moment, on the page I have an html table with rows that relate to a database table and another column with links to edit and delete the rows.
This is done by opening bootstrap modals containing a form which is populated for the appropriate row for the edit action and a delete modal to confirm the deletion, there is also a link on the page to add a new row, also via AJAX, so what I am trying to do is replicate this in react but I can't seem to understand how to go about it.
At the moment I have this (I am using a generic ponent name (ModelName
) for this example to avoid confusion from what it is actually called.):
var ModelNames = React.createClass({
getInitialState: function() {
return {model_names: this.props.model_names};
},
render: function() {
var rows = [];
this.props.model_names.forEach(function(model_name) {
rows.push(<ModelName model_name={model_name} key={model_name.id} />);
});
return (
<div className="row">
<div className="col-md-12">
<table className="table table-striped table-bordered table-hover">
<thead>
<tr role="row" className="heading">
<th>Property 1</th>
<th>Property 2</th>
<th className="text-center">Options</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
</div>
</div>
);
}
});
var ModelName = React.createClass({
handleRemoveModelName: function() {
$.ajax({
url: '/model_name/ajax_delete',
data: { model_name_id: this.props.model_name.id },
type: 'POST',
cache: false,
success: function(data) {
this.setState({ model_names: data }); // this is the part I am having trouble with
}.bind(this),
error: function() {
console.log('error');
}.bind(this)
});
},
render: function() {
return (
<tr id={"model_name_" + this.props.model_name.id}>
<td>{this.props.model_name.property_1}</td>
<td>{this.props.model_name.property_2}</td>
<td className="text-center">
<a href="javascript:;" className="btn btn-xs" onClick={this.handleEditModelName}><i className="fa fa-pencil"></i></a> <a href="javascript:;" className="btn btn-xs" onClick={this.handleRemoveModelName}><i className="fa fa-remove"></i></a>
</td>
</tr>
);
}
});
ReactDOM.render(
// this es from Rails
<ModelName model_names={<%= @model_names.to_json %>} />,
document.getElementById('react')
);
The deletion via ajax works fine and the server side responds with all the rows in the database table (after the deletion has taken place), so the json response is in exactly the same format as the initial data provided on the page load, I am just trying to figure out how to update the state so that react knows to remove the record I just deleted.
Similarly, I want to then extend it to replicate my editing and creation CRUD features but I am struggling to find the information I need after reading a number of blog posts.
Update
var ModelNames = React.createClass({
handleRemoveModelName: function(id) {
$.ajax({
url: '/model_name/ajax_delete',
data: { model_name_id: id },
type: 'POST',
cache: false,
success: function(data) {
this.setState({ model_names: data });
}.bind(this),
error: function() {
console.log('error');
}.bind(this)
});
},
render: function() {
var rows = [];
this.props.model_names.map(function(model_name) {
return (
<ModelName
model_name={model_name}
key={model_name.id}
onRemove={this.handleRemoveModelName}
/>
);
}, this);
// ...
}
});
var ModelName = React.createClass({
handleRemoveModelName: function() {
this.props.onRemove(this.props.model_name.id);
},
// ...
});
Also, just to try and include everything relevant, the delete button still has onClick={this.handleRemoveModelName}
for the ModelName
return
.
I've been tinkering with converting one page in my web application to use react rather than jQuery and Vanilla JS but I'm getting stuck on how to approach it because of how unfamiliar I am with react.
At the moment, on the page I have an html table with rows that relate to a database table and another column with links to edit and delete the rows.
This is done by opening bootstrap modals containing a form which is populated for the appropriate row for the edit action and a delete modal to confirm the deletion, there is also a link on the page to add a new row, also via AJAX, so what I am trying to do is replicate this in react but I can't seem to understand how to go about it.
At the moment I have this (I am using a generic ponent name (ModelName
) for this example to avoid confusion from what it is actually called.):
var ModelNames = React.createClass({
getInitialState: function() {
return {model_names: this.props.model_names};
},
render: function() {
var rows = [];
this.props.model_names.forEach(function(model_name) {
rows.push(<ModelName model_name={model_name} key={model_name.id} />);
});
return (
<div className="row">
<div className="col-md-12">
<table className="table table-striped table-bordered table-hover">
<thead>
<tr role="row" className="heading">
<th>Property 1</th>
<th>Property 2</th>
<th className="text-center">Options</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
</div>
</div>
);
}
});
var ModelName = React.createClass({
handleRemoveModelName: function() {
$.ajax({
url: '/model_name/ajax_delete',
data: { model_name_id: this.props.model_name.id },
type: 'POST',
cache: false,
success: function(data) {
this.setState({ model_names: data }); // this is the part I am having trouble with
}.bind(this),
error: function() {
console.log('error');
}.bind(this)
});
},
render: function() {
return (
<tr id={"model_name_" + this.props.model_name.id}>
<td>{this.props.model_name.property_1}</td>
<td>{this.props.model_name.property_2}</td>
<td className="text-center">
<a href="javascript:;" className="btn btn-xs" onClick={this.handleEditModelName}><i className="fa fa-pencil"></i></a> <a href="javascript:;" className="btn btn-xs" onClick={this.handleRemoveModelName}><i className="fa fa-remove"></i></a>
</td>
</tr>
);
}
});
ReactDOM.render(
// this es from Rails
<ModelName model_names={<%= @model_names.to_json %>} />,
document.getElementById('react')
);
The deletion via ajax works fine and the server side responds with all the rows in the database table (after the deletion has taken place), so the json response is in exactly the same format as the initial data provided on the page load, I am just trying to figure out how to update the state so that react knows to remove the record I just deleted.
Similarly, I want to then extend it to replicate my editing and creation CRUD features but I am struggling to find the information I need after reading a number of blog posts.
Update
var ModelNames = React.createClass({
handleRemoveModelName: function(id) {
$.ajax({
url: '/model_name/ajax_delete',
data: { model_name_id: id },
type: 'POST',
cache: false,
success: function(data) {
this.setState({ model_names: data });
}.bind(this),
error: function() {
console.log('error');
}.bind(this)
});
},
render: function() {
var rows = [];
this.props.model_names.map(function(model_name) {
return (
<ModelName
model_name={model_name}
key={model_name.id}
onRemove={this.handleRemoveModelName}
/>
);
}, this);
// ...
}
});
var ModelName = React.createClass({
handleRemoveModelName: function() {
this.props.onRemove(this.props.model_name.id);
},
// ...
});
Also, just to try and include everything relevant, the delete button still has onClick={this.handleRemoveModelName}
for the ModelName
return
.
2 Answers
Reset to default 3On top of Felix Kling's answer, you would need to somehow municate between the ModalNames
and ModalName
to update the state, since the state is managed by ModalNames
and the event is called in ModalName
.
One solution is to pass function that updates the state to the ModalName
:
ModalNames:
var ModelNames = React.createClass({
...
updateState: function(modal_names) {
this.setState({modal_names})
},
render: function() {
var rows = [];
this.state.model_names.forEach(function(model_name) {
rows.push(<ModelName model_name={model_name} key={model_name.id} updateState={this.updateState} />);
});
...
}
}
ModalName:
var ModelName = React.createClass({
handleRemoveModelName: function() {
$.ajax({
url: '/model_name/ajax_delete',
data: { model_name_id: this.props.model_name.id },
type: 'POST',
cache: false,
success: function(data) {
this.props.updateState(data);
}.bind(this),
error: function() {
console.log('error');
}.bind(this)
});
},
...
}
Alternative solution is to define handleRemoveModelName
in ModalNames
which takes in id, calls ajax, and does setState like you originally had.
You have to decide where the source of truth for your data should be. In your current setup, it appears to be in ModelNames
.
Instead of having ModelName
perform the deletion, let ModelNames
do it, since it owns the data.
Just pass a callback to ModelName
that is called when deletion is requested. In this example we are adding a new property to the child ponent, onRemove
which is called when the delete button is clicked:
var ModelNames = React.createClass({
handleRemoveModelName: function(id) {
// make Ajax call here
},
render: function() {
var rows = this.state.model_names.map(function(model_name) {
return (
<ModelName
model_name={model_name}
key={model_name.id}
onRemove={this.handleRemoveModelName}
/>
);
}, this);
// ...
}
});
var ModelName = React.createClass({
handleRemoveModelName: function() {
this.props.onRemove(this.props.model_name.id);
},
// ...
});
The parent ponent can then decide how it wants to respond to this and make the appropriate Ajax call.
This makes ModuleName
a "dump" ponent. It doesn't have any thing to do with data management and only renders the data it gets passed.
The other issue is that you are reading from the props in ModelNames
(this.props.model_names
), not the state. State and props are different. You need to read from the state (this.state.model_names
) in order for the ponent to update when the state changes.