Reputation: 5461
After reviewing a couple other somewhat similar posts, I'm still not sure why I am receiving the following error:
TypeError: _this3.deleteArticle is not a function
From what I gather, the issue is that deleteArticle
is not included in state
. However, I thought that the .bind(this)
should bind it to the state
thus resolving this issue. Why is this not working and what can I do to correct it?
The deleteArticle
function is being called in the render method via:
<td onClick={() => this.deleteArticle(article.Id).bind(this) }>
<Glyphicon glyph='trash' />
</td>
and looks as follows:
deleteArticle(id) {
fetch('https://localhost:44360/api/articles/' + id, {
method: 'DELETE'
}).then((response) => response.json())
.then((responseJson) => {
var deletedId = responseJson.id;
var index = this.state.articles.findIndex(function(o){
return o.id === deletedId;
})
if (index !== -1){
this.state.articles.splice(index, 1);
}
})
}
And the full component for the sake of being complete:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Glyphicon } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
export class ArticlesIndex extends Component {
displayName = ArticlesIndex.name
constructor(props) {
super(props);
this.state = { articles: [], loading: true };
fetch('https://localhost:44360/api/Articles/')
.then(response => response.json())
.then(data => {
this.setState({ articles: data, loading: false });
});
}
static renderArticlesTable(articles) {
return (
<table className='table'>
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Description</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{articles.map(article =>
<tr key={article.id}>
<td>{article.id}</td>
<td>{article.title}</td>
<td dangerouslySetInnerHTML={{ __html: article.description }}></td>
<td>
<LinkContainer to={'/articles/edit/' + article.id}>
<Glyphicon glyph='edit' />
</LinkContainer>
</td>
<td onClick={() => this.deleteArticle(article.Id).bind(this) }>
<Glyphicon glyph='trash' />
</td>
</tr>
)}
</tbody>
</table>
);
}
render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: ArticlesIndex.renderArticlesTable(this.state.articles);
return (
<div>
<h1>Articles</h1>
{contents}
</div>
);
}
deleteArticle(id) {
fetch('https://localhost:44360/api/articles/' + id, {
method: 'DELETE'
}).then((response) => response.json())
.then((responseJson) => {
var deletedId = responseJson.id;
var index = this.state.articles.findIndex(function(o){
return o.id === deletedId;
})
if (index !== -1){
this.state.articles.splice(index, 1);
}
})
}
}
Upvotes: 2
Views: 593
Reputation: 7492
A static
method is not bound to an instance of a class and will have a different context from a typical component.
The consequence of this is that other functions/variables in your class will not be accessible via the this
keyword, as their context is different.
Changing your function declaration :
static renderArticlesTable(articles)
To the following :
renderArticlesTable = articles =>
May solve your problem, as I don't see any reason for your function to be static. Also, making it an arrow function will automatically bind it to your class's context.
Your calls :
ArticlesIndex.renderArticlesTable(this.state.articles)
Will now be :
this.renderArticlesTable(this.state.articles)
I would also recommend changing your deleteArticle
function to be an arrow function that does not need to be bound :
deleteArticle = id => {
Also, do not set promise to trigger a setState
in your constructor. If your fetch
request sends out your data too early, you will set the state of an unmounted component. Use componentDidMount
when fetching data :
constructor(props) {
super(props);
this.state = { articles: [], loading: true };
}
componentDidMount(){
fetch('https://localhost:44360/api/Articles/')
.then(response => response.json())
.then(data => {
this.setState({ articles: data, loading: false });
});
}
While i'm at it, you can also put ternary conditions directly into your JSX :
render() {
const { loading, articles } = this.state
return (
<div>
<h1>Articles</h1>
{loading ?
<p><em>Loading...</em></p>
:
this.renderArticlesTable(articles)
}
</div>
);
}
I also noticed that you were attempting to modify your state directly in your deleteArticle
function. You cannot modify your state without using setState
.
To remove an item with a specific value, you can use filter
to take the item with the corresponding id
out of your previous state :
deleteArticle = id => {
fetch('https://localhost:44360/api/articles/' + id, {
method: 'DELETE'
}).then(response => response.json())
.then(({ id }) => { //Deconstructs you responseJson
this.sestState(prev => ({
articles: prev.articles.filter(article => article.id !== id)
}))
})
}
Upvotes: 3