I have a react/redux front-end that receives data via RabbitMQ. When a message arrives, our message handler will dispatch an action, e.g. PRODUCT_DETAILS_LOADED, that updates the store and renders the appropriate component with the product details. This is 95% working, but if I try to display nested object properties, I get an error: Cannot read property <prop> of undefined.
I have done some research on this. Most answers suggest that there's a race condition occurring, and that the component is trying to render the properties before they're "ready". I don't see how this can be the case, since I'm not making a REST call; I already have the data from the message queue and I've already updated the store. Moreover, a console.log inside the render function will show the correct values of the nested properties. Here's the bulk of my ProductDetails component:
const mapStateToProps = state => {
return { product: state.productDetail };
}
class ProductDetails extends Component {
render() {
console.log(this.props.product.availability.forsale); //This will correctly show 'true' or 'false'
return (
<div>
<h3>Product Details Page</h3>
<h5>Details for {this.props.product.name}: </h5>
<div>
Name: {this.props.product.name}
<br/>
SKU: {this.props.product.sku}
<br/>
Available: {this.props.product.availability.forsale} //This throws an error
<br/>
{/*Price: {this.props.product.price.$numberDecimal} //This would also throw an error
<br/>*/}
Description: {this.props.product.description}
</div>
<Link to="/">Back to Home Page</Link>
</div>
)
}
}
const Details = connect(mapStateToProps, null)(ProductDetails);
export default Details;
Here's a snippet from the handler that fires when a product details message is received:
} else if(json.fields.routingKey === 'product.request.get') {
if(json.status === 'success') {
let payload = json.data;
store.dispatch(productDetailsLoaded(payload));
}
Here's the function that gets dispatches the action:
export function productDetailsLoaded(details) {
return { type: PRODUCT_DETAILS_LOADED, productDetail: details }
}
And finally, the reducer:
if(action.type === PRODUCT_DETAILS_LOADED) {
return Object.assign({}, state, {
productDetail: action.productDetail
});
}
I've seen some answers that suggest something like the following to prevent the error: let av = this.props.product.availability ? this.props.product.availability.forsale : '';. This doesn't work for me. While it does prevent the error, I will display the blank value. This is not an option for me; I have to show correct information on the details page, not just "not-incorrect" information.
Can someone help me understand what's going on? Why does the console.log statement show the right data but the render statement immediately after bomb? And how can I fix this?
Thanks!
EDIT: Here's the object I'm trying to render:
{
assets: {
imgs: "https://www.zzzzzzz.com/content/12626?v=1551215311"
}
availability: {
forsale: true,
expires: "2025-12-29T05:00:00.000Z"
}
createdAt: "2015-01-25T05:00:00.000Z"
description: "his is a fake description"
modifiedAt: "2019-05-28T04:00:00.000Z"
name: "Product Name"
price: {
$numberDecimal: "59.95"
}
sku: "ZZZZ"
"title ": "Product Title"
_id: "5ceec82aa686a03bccfa67cc"
}