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

React路由跳转产品详情页返回后选新卡片显示旧数据useEffect失效

问题背景

重构旧代码库时需要实现:每次选中卡片跳转至对应产品详情页时,展示最新更新的页面内容,而非上次浏览的旧页面。

异常现象

现有产品卡片列表页每次点击卡片时会调用history.push方法,携带对应卡片ID作为路径参数跳转到产品详情页,但从浏览器点击返回回到列表页后,选择其他卡片跳转时,详情页始终展示上一次访问的旧数据,而非当前选中卡片的最新内容。尝试使用useEffect钩子实现数据更新重置未达到预期,原业务实现代码如下:

const [ dataLoad, holdRender ] = useState(false);
  const [ product, setProduct ] = useState({});
  const [ currentProduct, setCurrentProduct ] = useState(0);

  useEffect(()=> {
    (async () => {
      
      const productId = await props.getProduct(props.match.params.id);
      setCurrentProduct(productId);
    })();
return () => {
      if(!product){
        setProduct({})
      }
  }
  }, []);

  useEffect(() => {
    (async () => {
      const data = await props.security.product.data;
      setProduct(data);
    })();
    //if product.details is now available
    if(product?.details){
      //let go of render
      holdRender(true);
    }
     return () => {
//tried to reset here
        if(currentProduct !== 0){
          setCurrentProduct(0);
        }
    };
  }, [currentProduct, product]);

return(
    <div>
        {dataLoad !== false?
        <div>
          <Row>
            <Col>
              <Jumbotron id="productStatsCard">
                <div className="productStats">
                  <h2>{product? product.details_not_verified : <Loader/>}</h2>
                </div>
              </Jumbotron>
            </Col>
            <Col>
              <Jumbotron id="productStatsCard">
                <div className="productStats">
                  <h2>{product? product.details_verified: <Loader />}</h2>
                </div>
              </Jumbotron>
            </Col>
          </Row>
          <Row className="mt-5">
            <Col sm={8}>
              <Row>
                <Col>
                  <div className="pdtTableContainer">
                    <div>
                      <Table>
                        <thead>
                          <tr>
                            <th>Category</th>
                            <th>Product Id</th>
                            <th>Product Name</th>
                            <th>Product Type</th>
                          </tr>
                        </thead>
                        <tbody>
                          {dataLoad && product?.details ? product?.details.map(detail => (
                            <tr key={product.id}>
                              <td>
                              {product.category}
                              </td>
                              <td>{product.id}</td>
                              <td>
                                  {product.product_title}
                              </td>
                              <td>{product.product_type}</td>
                            </tr>
                          )) : 'No products to display'}
                        </tbody>
                      </Table>
                    </div>
                  </div>
                </Col>
              </Row>
            </Col>
          </Row>
        </div>: <Loader/>}
      </div>
  )
}

const mapStateToProps = (state) => ({
  security: state.security,
});

export default connect(mapStateToProps, { getProduct })(productOverview);
根因分析
  • 首屏数据拉取的useEffect依赖数组为空,仅在组件首次挂载时执行。同路由参数变化时组件不会被卸载重挂载,导致路由ID更新后不会重新触发新产品数据的拉取逻辑。
  • 多余的currentProduct状态加错误的依赖设置:第二个useEffect将product加入依赖数组,每次setProduct触发状态更新都会重复执行副作用,形成无意义的重渲染循环;清理函数中重置currentProduct为0的逻辑会打断正常的数据拉取流程。
  • 状态重置时序错误:路由参数变化时没有第一时间清空旧产品数据、重置加载状态,新数据拉取完成前页面会一直残留上一次访问的旧内容。
  • 数据绑定逻辑错误:拉取产品数据时没有和当前路由ID做绑定校验,直接读取Redux中缓存的产品数据,很容易拿到上一次请求留存的旧值。
  • 渲染逻辑存在冗余判断:dataLoad !== false的判断没有实际意义,遍历详情列表时统一用product.id作为key会出现重复key问题,导致列表渲染异常。
修复方案
  • 移除冗余的currentProduct状态,将路由参数props.match.params.id加入数据拉取useEffect的依赖数组,路由ID变化时自动触发新的数据拉取流程。
  • 副作用执行前先做状态重置:清空旧产品数据、将加载状态设为false展示全局加载器,避免旧数据残留。
  • 合并两个分散的useEffect逻辑,等产品接口请求完成、Redux中对应产品数据更新后,再同步数据到组件状态、放开加载渲染。
  • 删除错误的副作用清理逻辑,禁止在清理函数中重置当前流程依赖的状态值,避免打断正常请求。
  • 修正渲染层逻辑问题:移除冗余的dataLoad !== false判断,修复列表遍历的重复key问题,使用空值合并运算符替代原三目判断,避免合法falsy值被错误识别为未加载状态。

修复后的完整代码如下:

const [dataLoad, holdRender] = useState(false);
const [product, setProduct] = useState({});

useEffect(() => {
  // 路由ID变化时立即重置状态,清空旧数据展示加载态
  setProduct({});
  holdRender(false);

  (async () => {
    const currentProductId = props.match.params.id;
    // 拉取当前路由对应ID的产品数据
    await props.getProduct(currentProductId);
    // 数据拉取完成后同步Redux中的最新产品数据
    setProduct(props.security.product.data);
    holdRender(true);
  })();
}, [props.match.params.id, props.security.product.data]);

return(
  <div>
      {dataLoad ?
      <div>
        <Row>
          <Col>
            <Jumbotron id="productStatsCard">
              <div className="productStats">
                <h2>{product?.details_not_verified ?? <Loader/>}</h2>
              </div>
            </Jumbotron>
          </Col>
          <Col>
            <Jumbotron id="productStatsCard">
              <div className="productStats">
                <h2>{product?.details_verified ?? <Loader />}</h2>
              </div>
            </Jumbotron>
          </Col>
        </Row>
        <Row className="mt-5">
          <Col sm={8}>
            <Row>
              <Col>
                <div className="pdtTableContainer">
                  <div>
                    <Table>
                      <thead>
                        <tr>
                          <th>Category</th>
                          <th>Product Id</th>
                          <th>Product Name</th>
                          <th>Product Type</th>
                        </tr>
                      </thead>
                      <tbody>
                        {product?.details ? product.details.map((detail, index) => (
                          <tr key={detail.id ?? index}>
                            <td>{product.category}</td>
                            <td>{product.id}</td>
                            <td>{product.product_title}</td>
                            <td>{product.product_type}</td>
                          </tr>
                        )) : 'No products to display'}
                      </tbody>
                    </Table>
                  </div>
                </div>
              </Col>
            </Row>
          </Col>
        </Row>
      </div>: <Loader/>}
    </div>
)
}

const mapStateToProps = (state) => ({
  security: state.security,
});

export default connect(mapStateToProps, { getProduct })(productOverview);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:36:22