Reputation: 47
I recently start learning React. I have problem when use dispatch in UseEffect, which is inside the child in the loop. I can't publish all project, but below piece of code:
On the home page online store products are displayed. In a separate component there is a small card that is displayed using the map loop:
<Row className='mt-20'>
{products.map(product => (
<ProductItem key={product._id} product={product} history={history} />
))}
</Row>
Child component code:
const ProductItem = ({ product, history }) => {
const dispatch = useDispatch()
const reviewList = useSelector(state => state.reviewList)
const { reviews } = reviewList
useEffect(() => {
let clean = false
if (!clean) dispatch(listReviewsDetailsByProductId(product._id))
return () => (clean = true)
}, [dispatch, product])
const productRate =
reviews.reduce((acc, item) => item.rating + acc, 0) / reviews.length || 0
return (
<Col xl='3' sm='6'>
<Card>
<Card.Body>
<div
className={'product-img position-relative ' + styles.wrapperImage}
>
<Link to={'/product/' + product.slug}>
{product.images[0] && (
<img
src={product.images[0].path}
alt=''
className='mx-auto d-block img-fluid'
/>
)}
</Link>
</div>
<div className='mt-4 text-center'>
<h5 className='mb-3 text-truncate'>
<Link to={'/product/' + product.slug} className='text-dark'>
{product.name}{' '}
</Link>
</h5>
{reviews && (
<div className='mb-3'>
<StarRatingsCustom rating={productRate} size='14' />
</div>
)}
<ProductPrice price={product.price} newPrice={product.newPrice} />
</div>
</Card.Body>
</Card>
</Col>
)
}
I use dispatch action to get reviews for a specific product from the server and then calculate the rating and display it. Unfortunately, it works every other time, the rating appears, then disappears. I would be grateful for your help!
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 a useEffect cleanup function. in SingleProduct (created by Context.Consumer) in Route (at Root.jsx:96)
Upvotes: 0
Views: 185
Reputation: 42258
The problem is the clean
variable which is not a part of your component's state. It exists only inside the scope of the useEffect
callback and gets recreated every time that the effect runs.
What is the purpose of clean
and when should we set it to true
? The cleanup function of a useEffect
hook only runs when the component unmounts so that would not be the right place to set a boolean
flag like this.
In order to dispatch once per product, we can eliminate it and just rely on the dependencies array. I'm using product_.id
instead of product
so that it re-runs only if the id changes, but other property changes won't trigger it.
useEffect(() => {
dispatch(listReviewsDetailsByProductId(product._id))
}, [dispatch, product._id])
If this clean
state serves some purpose, then it needs to be created with useState
so that the same value persists across re-renders. You need to figure out where you would be calling setClean
. This code would call the dispatch only once per component even if the product
prop changed to a new product, which is probably not what you want.
const [clean, setClean] = useState(false);
useEffect(() => {
if (!clean) {
dispatch(listReviewsDetailsByProductId(product._id))
setClean(true);
}
}, [dispatch, product._id, clean, setClean])
Upvotes: 1