I am unable to render multiple images fetched from multiple calls to rest API in react native.
For Rest API reference, I am using woocommerce rest API for getting order details.
https://woocommerce.github.io/woocommerce-rest-api-docs/#retrieve-an-order
The problem is that order details don’t have primary image of line_items
in rest API. So I need to call each below product detail API for fetching product images for each line_item object via product_id
by again calling product details rest API.
https://woocommerce.github.io/woocommerce-rest-api-docs/#retrieve-a-product
Till now I have written the logic to call product details for each line_items but I am getting following error with my code. What would be the best way to handle this situation?
Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, the componentWillUnmount method,
Below is my implementation:
render() {
if (this.state.loading) {
return (
<View style={{ flex: 1, justifyContent: "center", alignContent: "center", padding: 20 }}>
<ActivityIndicator color='#96588a' size='large' />
</View>
)
}
return (
<ScrollView style={{ flex: 1 }}>
{this.displayOrderDataSection()}
{this.displayProductSection()}
{this.displayPaymentSection()}
{this.displayShippingDetailsSection()}
{this.displayBillingDetailsSection()}
</ScrollView>
);
}
getProductPrimaryImage = (productId) => {
let productData = null;
this.setState({ imageLoading: true });
let url = `${base_url}/wp-json/wc/v3/products/${productId}?consumer_key=${c_key}&consumer_secret=${c_secret}`
console.log(url);
fetch(url)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
imageLoading: false,
error: responseJson.code || null,
});
productData = responseJson
})
.then(() => {
return productData ?
((Array.isArray(productData.images) && productData.images.length) ?
productData.images[0].src : null)
: null;
})
.catch((error) => {
this.setState({
error,
imageLoading: false,
})
});
}
getLineItems = () => {
let itemArray = [];
orderData.line_items.forEach(item => {
let imgSrc = this.getProductPrimaryImage(item.product_id)
itemArray.push(
<View key={item.id} style={{ flex: 1, flexDirection: 'row', backgroundColor: 'white' }}>
<View style={{ flex: 1, justifyContent: "center", alignContent: "center" }}>
<Image source={imgSrc}
style={{ height: 100, width: 100 }} resizeMode='contain' />
</View>
<View style={{ flex: 2, marginTop: 10, marginBottom: 10, justifyContent: "center" }}>
<View style={{ marginLeft: 10 }}>
<Text>{item.name}</Text>
<Text>SKU: {item.sku}</Text>
<Text>Price: {this.getCurrencySymbol()}{item.price.toFixed(2)}</Text>
<Text>Oty: {item.quantity}</Text>
<View>{this.getTMProductOptions(item.meta_data)}</View>
</View>
</View>
</View>
)
})
return itemArray;
}
displayProductSection = () => {
return (
<View style={styles.section}>
<Text style={styles.titleText}>Product</Text>
{this.getLineItems()}
</View>
)
}
2
Answers
I really appreciated tmdesigned for giving me proper guidance. I learned the concept of react components again from this link.
So I solved my problem by chaining the subsequent fetch requests as a callback in setState which is called inside componentDidMount. Below is my implementation
The way to think about the render() method is that it may run repeatedly. In most cases it is re-run every time there’s a change that affects its output.
The way you have it structured, your render() function calls
{this.displayProductSection()}
which callsthis.getLineItems()
which callsthis.getProductPrimaryImage(item.product_id)
which makes the AJAX request to the WordPress API.Since render can (and likely will) be run repeatedly, this means your request for the image is being created repeatedly.
Running an AJAX request is not like displaying an image, where you put the src URL in the tag and the browser loads it once. HTML is parsed and run once, this is requesting it repeatedly.
A better pattern would be:
Sometimes you aren’t quite ready to fetch the remote data when the component mounts. Maybe it depends on some user input first. In that case, you could instead hook into componentDidUpdate. Check for your condition there, but since this function call also run repeatedly, also check the status and only request it if it hasn’t been requested yet.
In either case, notice the separation of concerns. Your render() function is doing one thing — displaying. It’s not kicking off network requests or triggering side effects. Your lifecycle methods (or functions that respond to user input) handle that stuff.