Reputation: 9865
Background
I am having a problem in my react app I am writing. I have been trying to remove a row from a table but I keep getting
setState is undefined
So basically this is how I have this setup.
This function will remove the row from the table. I have not tested this because I keep getting the
error
setState
of undefined
.
function removeRow(id) {
this.setState(state => {
state.tableData.splice(id, 1);
return {tableData: state.tableData};
});
}
Question
How do I correctly remove the tr
from the table
when the onClick method
fires of so that the state
will change and render
the table
again without the tr
that was removed?
Examples
This is the onClick
method which is calling the removeRow()
.
<td><a href="#" onClick={() => { removeRow(tableData[key].id) }}>Delete</a></td>
This is what it all looks like together,
var requests = [
{"id":1, "title":"Request from Nancy","updated_at":"2015-08-15 12:27:01 -0600","created_at":"2015-08-12 08:27:01 -0600","status":"Denied"}
]
class TableDisplay extends React.Component {
constructor() {
super();
this.state = {
tableData: requests
}
}
render() {
const {tableData} = this.props;
return (
<div className="table-wrapper">
<div><table className="table">
<tr className="separate"><td>Title</td><td>Status</td>
<td>Created</td><td>Updated</td><td>Delete</td></tr>
{Object.keys(tableData).map(function(key) {
function removeRow(id) {
this.setState(state => {
state.tableData.splice(id, 1);
return {tableData: state.tableData};
});
}
return // removed extra code to make it easy to read.
<td><a href="#" onClick={() => { removeRow(tableData[key].id) }}>Delete</a></td>
</tr>;
})}
</table>
</div>
</div>
);
}
}
I also tried to move the removeRow()
directly under the constructor
and above the render()
but when I do that then removeRow() throws an error when the onClick fires,
removeRow is not defined.
class TableDisplay extends React.Component {
constructor() {
super();
this.state = {
tableData: requests
}
}
// I have to remove "function" from the declaration or the app wont render from parse error.
removeRow(id) {
this.setState(state => {
state.tableData.splice(id, 1);
return {tableData: state.tableData};
});
}
render() {
const {tableData} = this.props;
return (
<div className="table-wrapper">
<div><table className="table">
<tr className="separate"><td>Title</td><td>Status</td>
<td>Created</td><td>Updated</td><td>Delete</td></tr>
{Object.keys(tableData).map(function(key) {
return // removed extra code to make it easy to read.
<td><a href="#" onClick={() => { removeRow(tableData[key].id) }}>Delete</a></td>
</tr>;
})}
</table>
</div>
</div>
);
}
}
Upvotes: 2
Views: 15149
Reputation: 11
**Put removeRow method above render() and give current scope reference as var that = this; **
class TableDisplay extends React.Component {
constructor() {
super();
this.state = {
selectValue: 'all',
tableData: requests
}
}
removeRow() {
this.setState(state => {
state.tableData.splice(id, 1);
return {tableData: state.tableData};
});
}
render() {
const {selectValue} = this.props;
const {tableData} = this.props;
var that = this;
return (
<div className="table-wrapper">
<div><table className="table">
<tr className="seperate"><td>Title</td><td>Status</td><td>Created</td><td>Updated</td><td>Delete</td></tr>
{Object.keys(tableData).map(function(key) {
let styling = "bg-plain";
let hidden = "hidden";
let tds = document.querySelectorAll(".table td");
let trs = document.querySelectorAll(".table tr");
function showHiddenRows() {
for (var i = 0; i < trs.length; i++) {
trs[i].classList.remove("hidden");
}
}
function setStyle() {
if (tableData[key].status === "Approved") {
styling = "bg-success";
} else if (tableData[key].status === "Denied") {
styling = "bg-danger";
}
for (var i = 0; i < tds.length; i++) {
if (tds[i].innerText === "Approved") {
tds[i].parentNode.setAttribute('class', 'bg-success')
} else if (tds[i].innerText === "Denied") {
tds[i].parentNode.setAttribute('class', 'bg-danger')
}
}
}
function filterTable(fa, fb) {
for (var i = 0; i < tds.length; i++) {
if (tds[i].innerText === fa) {
tds[i].parentNode.setAttribute('class', 'hidden')
} else if (tds[i].innerText === fb) {
tds[i].parentNode.setAttribute('class', 'hidden')
}
}
}
if (selectValue === "all") {
showHiddenRows();
setStyle();
} else if (selectValue === "approved") {
showHiddenRows();
setStyle();
filterTable("Pending", "Denied");
} else if (selectValue === "pending") {
showHiddenRows();
setStyle();
filterTable("Approved", "Denied");
} else if (selectValue === "denied") {
showHiddenRows();
setStyle();
filterTable("Approved", "Pending");
}
{console.log(tableData[key].id)}
return <tr key={tableData[key].id} id={tableData[key].id} className={styling}>
<td>{tableData[key].title}</td>
<td>{tableData[key].status}</td>
<td>{tableData[key].created_at.slice(0, 10)}</td>
<td>{tableData[key].updated_at.slice(0, 10)}</td>
<td><a href="#" onClick={() => { that.removeRow() }}>Delete</a></td>
</tr>;
})}
</table>
</div>
</div>
);
}
}
Upvotes: 1
Reputation: 281626
When you call removeRow
in the second solution, you need to call it like this.removeRow
since it is outside the render function. Also you need to bind it so that you can access setState
. For this purpose you can use the arrow function
class TableDisplay extends React.Component {
constructor() {
super();
this.state = {
tableData: requests
}
}
// I have to remove "function" from the declaration or the app wont render from parse error.
removeRow = (id) => {
this.setState(state => {
state.tableData.splice(id, 1);
return {tableData: state.tableData};
});
}
render() {
const {tableData} = this.props;
return (
<div className="table-wrapper">
<div><table className="table">
<tr className="separate"><td>Title</td><td>Status</td>
<td>Created</td><td>Updated</td><td>Delete</td></tr>
{Object.keys(tableData).map(function(key) {
return // removed extra code to make it easy to read.
<td><a href="#" onClick={() => { this.removeRow(tableData[key].id) }}>Delete</a></td>
</tr>;
}.bind(this))}
</table>
</div>
</div>
);
}
}
Upvotes: 1
Reputation: 5402
The context of this is lost.
You can bind function to this
.
class TableDisplay extends React.Component {
constructor() {
super();
this.state = {
tableData: requests
}
//ADD THIS LINE
this.removeRow = this.removeRow.bind(this);
}
// I have to remove "function" from the declaration or the app wont render from parse error.
removeRow(id) {
this.setState(state => {
state.tableData.splice(id, 1);
return {tableData: state.tableData};
});
}
......
<a href="#" onClick={() => { this.removeRow(tableData[key].id) }}
Or alternatively if you are using ES6 Stage 2 preset
, you can use arrow functions
class TableDisplay extends React.Component {
constructor() {
super();
this.state = {
tableData: requests
}
}
// I have to remove "function" from the declaration or the app wont render from parse error.
removeRow = (id) => {
this.setState(state => {
state.tableData.splice(id, 1);
return {tableData: state.tableData};
});
}
......
Upvotes: 2
Reputation: 1267
There are a few issues with your component, namely the fact that you don't bind the context in the remove function (hence setState
is undefined). Try something like this:
const TableRow = (props) => (
<tr>
<td>{props.title}</td>
<td>{props.status}</td>
<td>{props.updated_at}</td>
<td>{props.created_at}</td>
<td><a href="#" onClick={() => props.remove(props.index)}>Delete</a></td>
</tr>
)
class TableDisplay extends React.Component {
constructor() {
super();
this.state = {
tableData: [{
"id":1,
"title":"Request from Nancy",
"updated_at":"2015-08-15 12:27:01 -0600",
"created_at":"2015-08-12 08:27:01 -0600",
"status":"Denied"
}]
};
}
remove(index) {
this.setState({
tableData: this.state.tableData.filter((row, i) => i !== index)
});
}
render() {
return (
<div className="table-wrapper">
<div>
<table className="table">
<tbody>
{
this.state.tableData.map((row, i) => {
return (
<TableRow
title={row.title}
id={row.id}
updated_at={row.updated_at}
created_at={row.created_at}
status={row.status}
remove={this.remove.bind(this)}
index={i}
key={i} />
);
})
}
</tbody>
</table>
</div>
</div>
);
}
}
ReactDOM.render((
<TableDisplay />
), document.getElementById("app"));
Upvotes: 3