useState setter 方法不更新数组

Posted

技术标签:

【中文标题】useState setter 方法不更新数组【英文标题】:useState setter method not updating array 【发布时间】:2019-10-31 10:27:06 【问题描述】:

我正在使用 react 钩子在 react 中编写一个谷歌地图组件,并且我正在将状态中的谷歌地图标记和多边形数组存储为一个称为叠加层的数组。当我从地图中清除覆盖时,我用一个空数组调用 setOverlays,但状态永远不会改变。

到目前为止,我已经尝试传递一个空数组,以及我已经从中弹出所有值的现有数组的副本。两者都没有奏效。此外,我尝试将我正在使用的 3 个 useState 调用合并到一个单一的状态对象中,但也遇到了同样的问题。

const Map = ( activeFilter ) => 
  const [map, setMapState] = useState(null);
  const [maps, setMapsState] = useState(null);
  const [overlays, setOverlays] = useState([]);

  useEffect(() => 
    // console.log('filter - updated');
    drawMapFeatures();
  , [activeFilter]);

  useEffect(() => 
    // console.log('maps - updated');
    drawParks();
    drawMapFeatures();
  , [maps]);

  const setUpMap = (map, maps) => 
    setMapState(map);
    setMapsState(maps);
  ;

  const drawMapFeatures = () => 
    switch (activeFilter) 
      case 'Our Buildings':
        clearOverlays();
        drawNeighborhoods();
        drawBuildings();
        break;
      case 'Restaurants':
        clearOverlays();
        drawMarkers(places['RESTAURANTS']);
        break;
      case 'Grab & Go Food':
        clearOverlays();
        drawMarkers(places['GRAB & GO FOOD']);
        break;
      case 'Event Spaces':
        clearOverlays();
        drawMarkers(places['EVENT SPACES']);
        break;
      case 'Bars':
        clearOverlays();
        drawMarkers(places['BARS']);
        break;
      case 'Cafes + Bakeries':
        clearOverlays();
        drawMarkers(places['CAFES + BAKERIES']);
        break;
      case 'Retail':
        clearOverlays();
        drawMarkers(places['RETAIL']);
        break;
      case 'Health + Fitness':
        clearOverlays();
        drawMarkers(places['HEALTH + FITNESS']);
        break;
      case 'Galleries + Museums':
        clearOverlays();
        drawMarkers(places['GALLERIES + MUSEUMS']);
        break;
      case 'Film, Theater And Culture':
        clearOverlays();
        drawMarkers(places['FILM, THEATER AND CULTURE']);
        break;
      case 'Bank And Convenience':
        clearOverlays();
        drawMarkers(places['BANK AND CONVENIENCE']);
        break;
      default:
        break;
    
  ;

  const drawParks = () => 
    if (map && maps) 
      parks.forEach(park => 
        const tempPark = new maps.Polygon(
          paths: park,
          strokeColor: '#afc47b',
          strokeOpacity: '1',
          strokeWeight: 1,
          fillOpacity: '0.8',
          fillColor: '#afc47b'
        );
        tempPark.setMap(map);
      );
     else 
      // console.log('maps not setup');
     
  ;

  const drawBuildings = () => 
    if (map && maps) 
      const buildingsArray = overlays.slice(0);
      ourBuildings.forEach(building => 
        const tempBuilding = new maps.Polygon(
          paths: building.path,
          strokeColor: '#369BF7',
          strokeOpacity: '1',
          strokeWeight: 0.5,
          fillOpacity: '1',
          fillColor: '#369BF7'
        );
        const tempMarker = new maps.Marker(
          position: building.markerPos,
          icon:  url: building.markerImg, scaledSize: new maps.Size(90, 60), anchor: new maps.Point(45, 30) 
        );
        tempBuilding.setMap(map);
        tempMarker.setMap(map);
        buildingsArray.push(tempBuilding);
        buildingsArray.push(tempMarker);
      );
      if (overlays !== buildingsArray) 
        // console.log('buildings');
        setOverlays(buildingsArray);
      
    
  ;

  const drawMarkers = data => 
    const markersArray = overlays.slice(0);
    if (map && maps) 
      Object.keys(data).forEach(key => 
        const tempMarker = new maps.Marker(
          icon: 
            path: 'M0,4a4,4 0 1,0 8,0a4,4 0 1,0 -8,0',
            fillColor: '#369bf7',
            fillOpacity: 0.95,
            scale: 1.5,
            strokeColor: '#000000',
            strokeWeight: 1,
            anchor: new maps.Point(-1, 4),
            labelOrigin: new maps.Point(4, 15)
          ,
          position: data[key].position,
          label:  color: '#000000', fontWeight: 'bold', fontSize: '12px', text: data[key].name 
        );
        tempMarker.setMap(map);
        markersArray.push(tempMarker);
      );
    
    if (overlays !== markersArray) 
      // console.log('markers');
      setOverlays(markersArray);
    
  ;

  const drawNeighborhoods = () => 
    if (map && maps) 
      const neighborhoodArray = overlays.slice(0);
      neighborhoodOverlays.forEach(neighborhood => 
        const tempNeighborhood = new maps.Polygon(
          paths: neighborhood.path,
          strokeColor: '#369BF7',
          strokeOpacity: 0,
          fillOpacity: 0,
          strokeWeight: 0,
          fillColor: '#369BF7',
          zIndex: 100
        );
        maps.event.addListener(tempNeighborhood, 'mouseover', function() 
          this.setOptions( fillOpacity: '0.5' );
        );
        maps.event.addListener(tempNeighborhood, 'mouseout', function() 
          this.setOptions( fillOpacity: '0' );
        );
        tempNeighborhood.setMap(map);
        neighborhoodArray.push(tempNeighborhood);
      );
      if (overlays !== neighborhoodArray) 
        // console.log('neighborhoods');
        // console.log(neighborhoodArray.length);
        setOverlays(neighborhoodArray);
      
    
  ;

  const clearOverlays = () => 
    if (overlays.length > 0) 
      const overlaysCopy = overlays.slice(0);
      // debugger;
      while (overlaysCopy.length > 0) 
        const overlay = overlaysCopy.pop();
        overlay.setMap(null);
      
      // console.log('clear');
      // console.log(overlaysCopy.length);
      setOverlays(overlaysCopy);
      // console.log('overlays', overlays.length);
    
  ;

  return (
    <MapContainer>
      <GoogleMapReact
        bootstrapURLKeys= key: 'AIzaSyBSsLXxJ5NSrSgFjFW7U5hxmGyHnE1po88' 
        defaultCenter=
          lat: 40.726,
          lng: -74.006
        
        defaultZoom=16
        options=mapOptions
        yesIWantToUseGoogleMapApiInternals
        onGoogleApiLoaded=( map, maps ) => setUpMap(map, maps)
      />
    </MapContainer>
  );
;

export default Map;

理想情况下,当使用空数组调用 setOverlays 时,覆盖状态将重置为默认值。

【问题讨论】:

【参考方案1】:

我确定有太多的 setState 操作发生得太近了。为了解决这个问题,我重构了一个类组件,这样我就可以使用 this.setState 和可选的第二个参数以程序方式清除状态并重置。

【讨论】:

【参考方案2】:

尝试改变:

  const [map, setMapState] = useState(null);
  const [maps, setMapsState] = useState(null);

  const [mapState, setMapState] = useState(null);
  const [mapsState, setMapsState] = useState(null);

【讨论】:

我会这样做,但问题不在于其中任何一个。问题出在 [overlays, setOverlays] useState

以上是关于useState setter 方法不更新数组的主要内容,如果未能解决你的问题,请参考以下文章

在JS中数组内部值的变化不触发视图更新的浅谈

react usestate 重新设置数据后,不渲染页面

React Hook useState 视图不更新

useState 数组在创建重复项时未正确更新

从另一个屏幕调用函数时 useState 数组未更新

如何在ReactState中使用其索引更新useState中的数组