You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Hooks组件如何清理订阅?解决异步更新内存泄漏与重复渲染问题

修复方案

问题根源

  1. 无限重复渲染:HomeScreen组件的useEffect依赖数组错误包含了products状态。你在useEffect内部调用setProducts会修改products值,而products变更会再次触发useEffect执行,形成死循环,导致重复请求、重复渲染。
  2. 内存泄漏警告:未处理异步请求的中断逻辑,若组件在请求返回前卸载,请求完成后依然会尝试更新已卸载组件的状态,触发警告。

修复步骤

  1. 修正HomeScreen的useEffect依赖数组为空白数组,确保仅在组件首次挂载时发起一次请求。
  2. 两个组件的请求逻辑新增AbortController中断处理,在useEffect清理函数中终止未完成的请求,避免组件卸载后执行状态更新。
  3. ProductScreen的useEffect依赖调整为match.params.id,比依赖完整match对象更稳定,避免不必要的重渲染。

修复后代码

HomeScreen.jsx

import React , { useState, useEffect } from 'react'
import { Row , Col } from 'react-bootstrap'
import Product from '../components/Product'
import axios from 'axios'

const HomeScreen = () =>{
    const [products , setProducts]  = useState([])
    useEffect(()=>{
        const controller = new AbortController()
        const fetchProducts = async () =>{
            try {
                const {data} = await axios.get('http://localhost:5000/api/products', {
                    signal: controller.signal
                })
                setProducts(data)
            } catch (err) {
                // 忽略请求主动中断的报错
                if (err.name !== 'CanceledError') {
                    console.error(err)
                }
            }
        }
        fetchProducts()
        // 组件卸载时中断未完成的请求
        return () => controller.abort()
    },[])

    return(
        <>
        <h2 className='my-3'>Latest Products</h2>
        <Row>
            {
                products.map((product)=>(
                    <Col key={product._id} sm={12} md={6} lg={4} xl={3}>
                    <Product product={product} rating = {product.rating} reviews={product.numReviews}/>
                    </Col>
                ))
            }
        </Row>
        </>
    )
}
export default HomeScreen

ProductScreen.jsx

import React , {useState , useEffect} from 'react'
import { Row, Col, Image, ListGroup, Card, Button } from 'react-bootstrap'
import {Link} from 'react-router-dom'
import Rating from '../components/Rating'
import axios from 'axios'


const ProductScreen = ({match}) => {
    const [product , setProduct] = useState({})
    useEffect(()=>{
        const controller = new AbortController()
        const fetchProduct = async ()=>{
            try {
                const {data} = await axios.get(`http://localhost:5000/api/products/${match.params.id}`, {
                    signal: controller.signal
                })
                setProduct(data)
            } catch (err) {
                if (err.name !== 'CanceledError') {
                    console.error(err)
                }
            }
        }
        fetchProduct()
        return () => controller.abort()
    },[match.params.id])

    return (
        <>
            <Link className='btn btn-light my-3' to='/'>
            Go Back
            </Link>
            <Row>
                <Col md={6}>
                <Image src={product.image} alt={product.name} />
                </Col>
                <Col md={3}>
                    <ListGroup variant='flush'>
                        <ListGroup.Item>
                <h3>{product.name}</h3>
                        </ListGroup.Item>

                <ListGroup.Item>
                    <Rating rating={product.rating} reviews={product.numReviews}/>
                </ListGroup.Item>
                <ListGroup.Item>
                <strong> Price: ${product.price}</strong>
                </ListGroup.Item>
                <ListGroup.Item>
                    <strong>Description :</strong> {product.description}
                </ListGroup.Item>
                    </ListGroup>
                </Col>
                <Col md={3}>
                    <Card>
                        <ListGroup>
                            <ListGroup.Item>
                                <Row>
                                    <Col>
                                    Price :
                                    </Col>
                                    <Col>
                                    <strong>${product.price}</strong>
                                    </Col>
                                </Row>
                            </ListGroup.Item>
                            <ListGroup.Item>
                                <Row>
                                    <Col>
                                    Status :
                                    </Col>
                                    <Col>
                                    <strong>{product.countInStock > 0 ? 'In Stock' : "Out Of Stock"}</strong>
                                    </Col>
                                </Row>
                            </ListGroup.Item>

                            <ListGroup.Item className='d-grid gap-2'>
                                <Button type='button' disabled={product.countInStock === 0}>
                                    Add To Cart
                                </Button>
                            </ListGroup.Item>
                        </ListGroup>
                    </Card>
                
                </Col>
            </Row>
        </>
    )
}

export default ProductScreen

内容的提问来源于stack exchange,提问作者Mohammed Alhawamdeh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 14:36:02