In my example, I am fetching a Product-Object from an API. The Product has ImageIds, referring to Images, which I also fetch from an API after the product is fetched.
class ProductDetails extends Component {
state = {
productId: this.props.match.params.id,
product: null,
imagePaths: [],
currentImagePath: null,
loading: true
}
constructor(props) {
super(props);
this.request = getDefaultRequest();
}
componentDidMount = () => {
this.fetchProduct();
}
// Api calls
fetchProduct = async () => {
const response = await this.request.get(`http://localhost:8080/product/Id=${this.state.productId}`);
let { product } = response.data;
this.setState({
product
}, () => {
this.fetchImages().then((images) => {
this.setImagePaths(images);
});
});
}
fetchImages = async () => {
let { product } = this.state;
let images = [];
for (let i = 0; i < product.ProductImageIds.length; i++) {
let currentProducImageId = product.ProductImageIds[i];
let response = await this.request.get(`http://localhost:8080/productImage/Id=${currentProducImageId}`);
images.push(response.data.productImage);
}
return Promise.resolve(images);
}
setImagePaths = (images) => {
let imagePaths = images.map(image => {
return image.absolutePath.split("public")[1].replaceAll("\\", "/");
});
this.setState({
imagePaths,
currentImagePath: imagePaths[0],
loading: false
});
}
render() {
let { product, loading } = this.state;
if (!loading) {
return (
<div>
<h2 className="t-center">{product.name}</h2>
<div className="wrap-product-details">
<ProductQuickWatch
imagePaths={this.state.imagePaths}
currentImagePath={this.state.currentImagePath}
productName={this.state.product.name}
productId={this.state.product._id}
orientation="vertical">
</ProductQuickWatch>
</div>
</div>
);
}
return (
<LoadingCircle
usePortal={true}
loading={true} />
)
}
But now I get "TypeError: Cannot read property 'state' of undefined"
I debugged this and realized that everything works fine until setting state of loading to false (in setImagePaths()).
If I omit setting loading to false, everything works fine. But I don't know why. And loading state is important if you look at render().
I researched about this and a possible solution is to bind the functions in the constructor. But usually if you use arrow-syntax "this" isn't a problem. And I also tried it and it didn't work :/.
The error:
TypeError: Cannot read property 'state' of undefined
new i
C:/Users/David/Documents/GitHub/React/ShoppingTemplate/src/Store/WithStore.jsx:22
19 | children: null,
20 | };
21 |
> 22 | constructor(props, context) {
| ^ 23 | super(props, context);
24 | this.state = mapStateToProps({ ...context.state });
25 | this.updateStateProps = this.updateStateProps.bind(this);
▶ 19 stack frames were collapsed.
ProductDetails.setImagePaths
C:/Users/David/Documents/GitHub/React/ShoppingTemplate/app/src/Components/Category/ProductDetails.jsx:47
44 | let imagePaths = images.map(image => {
45 | return image.absolutePath.split("public")[1].replaceAll("\\", "/");
46 | });
> 47 | this.setState({
| ^ 48 | imagePaths,
49 | currentImagePath: imagePaths[0],
50 | loading: false
View compiled
(anonymous function)
C:/Users/David/Documents/GitHub/React/ShoppingTemplate/app/src/Components/Category/ProductDetails.jsx:28
25 | product
26 | }, () => {
27 | this.fetchImages().then((images) => {
> 28 | this.setImagePaths(images);
| ^ 29 | });
30 | });
31 | }