Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
154 views
in Technique[技术] by (71.8m points)

javascript - Dispatch works every other time in children in a loop in useeffect

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)

question from:https://stackoverflow.com/questions/65878899/dispatch-works-every-other-time-in-children-in-a-loop-in-useeffect

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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])

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...