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

React useCallback钩子疑问:依赖布尔值未变化时函数仍重复渲染的问题排查

解决useCallback缓存后仍重复渲染标记的问题

看起来你遇到的核心问题是虽然用了useCallback缓存渲染函数,但每次组件渲染时还是会执行这些函数并生成新的JSX,导致标记反复渲染。让我一步步帮你拆解问题并给出解决方案:

问题根源分析

  1. 依赖数组不完整(闭包陷阱)
    你的renderPlacesMarkers函数里用到了places数组,但依赖数组里只加了showPlaces——这会导致两个问题:

    • 当places更新时,函数还是会引用闭包中的旧places值,显示错误的标记
    • 即使showPlaces没变,Map组件因其他状态(比如places)更新而重新渲染时,调用renderPlacesMarkers()会生成基于新places的JSX,但因为函数没缓存新的places,反而会导致不必要的渲染逻辑执行
  2. 直接调用缓存函数仍会生成新JSX
    即使useCallback缓存了函数本身,每次Map组件渲染时调用{renderPlacesMarkers()},都会执行函数内部的places.map(...),生成全新的JSX元素数组。GoogleMap组件会对比子元素的引用,每次都是新数组,所以会重新渲染所有标记。

具体修复步骤

第一步:补全useCallback的依赖数组(必须先做)

首先要把函数中用到的所有外部变量都加入依赖数组,避免闭包问题:

const renderPlacesMarkers = useCallback(() => {
  console.log('rendering places')
  if(showPlaces){
   return places.map(place => (
    <Marker icon={icon} key={place.ID} title={place.Name} position={{lat:place.Latitude,lng:place.Longitude}}/>
   ))
  }
  return null;
},[showPlaces, places, icon]); // 补全places和icon

const renderPlaces2Markers = useCallback(() => {
  console.log('rendering places2')
  if(showPlaces2){
   return places2.map(place => (
    <Marker key={place.ID} title={place.ID} position={{lat:place.Latitude,lng:place.Longitude}}/>
   ))
  }
  return null;
},[showPlaces2, places2]); // 补全places2

const renderHeatMap = useCallback(() => {
  console.log('rendering heat map')
  if(showHeatMap){
   const mapData = heatMap.map(location => ({
    location: new google.maps.LatLng(location.Latitude,location.Longitude),
    weight: location.population_density
   }))
   return (<HeatmapLayer data={mapData} options={{gradient: gradient}} />)
  }
  return null;
},[showHeatMap, heatMap, gradient]); // 补全heatMap和gradient

第二步:用memo组件替代渲染函数(核心优化)

直接调用缓存函数无法避免生成新JSX,更好的方式是把渲染逻辑封装成独立的memo组件,让React自动对比props来决定是否重新渲染:

// 在Map组件外部定义(或内部,建议外部避免重复创建)
const PlacesMarkers = React.memo(({ show, places, icon }) => {
  console.log('rendering places');
  if (!show) return null;
  return places.map(place => (
    <Marker 
      icon={icon} 
      key={place.ID} 
      title={place.Name} 
      position={{ lat: place.Latitude, lng: place.Longitude }}
    />
  ));
});

const Places2Markers = React.memo(({ show, places }) => {
  console.log('rendering places2');
  if (!show) return null;
  return places.map(place => (
    <Marker 
      key={place.ID} 
      title={place.ID} 
      position={{ lat: place.Latitude, lng: place.Longitude }}
    />
  ));
});

const HeatMapLayerWrapper = React.memo(({ show, heatMap, gradient }) => {
  console.log('rendering heat map');
  if (!show) return null;
  const mapData = heatMap.map(location => ({
    location: new google.maps.LatLng(location.Latitude, location.Longitude),
    weight: location.population_density
  }));
  return <HeatmapLayer data={mapData} options={{ gradient }} />;
});

然后在Map组件的GoogleMap中使用这些组件:

<GoogleMap mapContainerStyle={containerStyle} center={center} zoom={10} onLoad={onLoad} onUnmount={onUnmount}>
  <PlacesMarkers show={showPlaces} places={places} icon={icon} />
  <Places2Markers show={showPlaces2} places={places2} />
  <HeatMapLayerWrapper show={showHeatMap} heatMap={heatMap} gradient={gradient} />
</GoogleMap>

第三步:优化Marker组件(可选但推荐)

如果使用的Marker组件没有自带memo优化,你可以自己包裹一层:

const MemoizedMarker = React.memo(Marker);

然后在上面的PlacesMarkers等组件中使用MemoizedMarker代替Marker,这样单个标记只有当props(比如位置、标题)变化时才会重新渲染。

为什么这样有效?

  • React.memo会对组件的props做浅比较,只有当props(比如showPlaces、places)变化时,才会重新渲染组件并生成新的JSX
  • 避免了每次Map组件渲染时都执行渲染函数,只有必要时才会更新标记

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 21:14:04