簡體   English   中英

重新渲染后反應 Leaflet 彈出窗口打開

[英]React Leaflet popup open after rerender

即使在重新渲染后,我也想讓我的標記彈出窗口打開。 我有一個顯示用戶 position 的標記,以便一直更新。 在該標記重新呈現后關閉其他標記的彈出窗口。

我的代碼:



const StaticReferencePoints = (props) => {
  const { selectedCalibrationPoint, setSelectedCalibrationPoint, staticReferencePoints } = props;
  const addToast = useToastContext();
  const [snackbarOpen, setSnackbarOpen] = useState(true);
  const [latitude, longitude] = useGeolocationStoreHelper(['latitude', 'longitude']);
  const [map, setMap] = useState(null);

  const center = [latitude, longitude];
  const markerRef = useRef({});
  const [selectedMarker, setSelectedMarker] = useState(null);

  useEffect(() => {
    // open popup if it exists
    if (selectedMarker) {
      markerRef.current[selectedMarker]?.openPopup();
    }
  }, [selectedMarker, latitude, longitude]);

  useEffect(() => {
    if (map) {
      map.addEventListener('click', () => {
        setSelectedMarker(null);
      });
    }
  }, [map]);

  const handleSnackbarClose = (event, reason) => {
    if (reason === 'clickaway') {
      return;
    }
    setSnackbarOpen(false);
  };

  const handleDeletePoint = (point) => {
    if (confirm('Are you sure you want to delete this point?')) {
      Meteor.call(...) => {
        if (err) {
          console.log(err);
          addToast('Error deleting point', 'error');
        }
        if (res) {
          console.log(res);
          addToast('Point deleted', 'success');
        }
      });
    }
  };

  const SingleMarker = (point) => {
    const [anchorEl, setAnchorEl] = useState(null);
    const handleClick = (event) => {
      setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
      setAnchorEl(null);
    };
    return (
      <Marker
        key={'staticPoint-' + point.point?.name}
        position={point.point?.coordinates}
        ref={(el) => (markerRef.current[point.point.name] = el)}
        eventHandlers={{
          click: () => {
            setSelectedMarker(point.point.name);
          },
        }}
      >
        <Popup options={{ autoClose: false }}>
          <Grid container item justifyContent={'center'} xs={12}>
            <Grid item container direction={'row'} xs={12}>
              <Grid item xs={1} container justifyContent={'center'} alignItems={'center'}>
                {ACL.check) && (
                  <div>
                    <IconButton size="small" onClick={handleClick}>
                      <MoreVertIcon />
                    </IconButton>
                    <Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
                      <MenuItem onClick={() => handleDeletePoint(point.point)}>
                        <ListItemIcon>
                          <DeleteIcon />
                        </ListItemIcon>
                        <ListItemText primary="Delete" />
                      </MenuItem>
                    </Menu>
                  </div>
                )}
              </Grid>
              <Grid item xs={10} container justifyContent={'center'} alignItems={'center'}>
                <Typography>{point.point?.name}</Typography>
              </Grid>
              <Grid item xs={1}></Grid>
            </Grid>
            <Button
              onClick={() => {
                setSelectedCalibrationPoint(point.point);
                setSelectedMarker(null);
              }}
            >
              Select For Calibration
            </Button>
          </Grid>
        </Popup>
      </Marker>
    );
  };

  const StaticPointMarkers = () => {
    return staticReferencePoints.map((point, i) => {
      return <SingleMarker key={'marker-' + i} point={point} />;
    });
  };

  const UserPositionMarker = () => <Marker position={[latitude, longitude]} icon={userPosition}></Marker>;

  return (
    <Grid item xs={12}>
      <Snackbar
        anchorOrigin={{
          vertical: 'top',
          horizontal: 'center',
        }}
        open={snackbarOpen}
        onClose={handleSnackbarClose}
        autoHideDuration={5000}
        message="Calibrate first by selecting a point from the map"
        action={
          <IconButton size="small" aria-label="close" color="inherit" onClick={handleSnackbarClose}>
            <Close fontSize="small" />
          </IconButton>
        }
      />
      <MapContainer
        center={center}
        zoom={18}
        maxZoom={28}
        scrollWheelZoom={false}
        style={{ height: '65vh' }}
        whenCreated={setMap}
      >
        <TileLayer
          url="https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}"
          maxZoom={28}
          subdomains={['mt0', 'mt1', 'mt2', 'mt3']}
        />
        {staticReferencePoints && <StaticPointMarkers />}
        {latitude && longitude && <UserPositionMarker />}
      </MapContainer>
      <Grid id={'info'} item container xs={12} justifyContent={'center'} marginTop={'1rem'}>
        <Typography>{`${latitude}, ${longitude}`}</Typography>
      </Grid>
      <Grid id={'info'} item container xs={12} justifyContent={'center'}>
        <InfoIcon onClick={() => setSnackbarOpen(true)} />
      </Grid>
    </Grid>
  );
};

export default StaticReferencePoints;

我現在已經在 state 上設置了當前打開的彈出窗口,並在 useEffect 上打開了彈出窗口,但這會產生閃爍。

強制彈出窗口保持打開狀態的最佳方法是什么?

如果我理解你的問題,問題是當用戶位置改變時,選定的標記彈出窗口閃爍?

一般來說,只要不再次創建標記,彈出窗口就應該能夠保持打開狀態。

我可以看到您將latitudelongitude作為 useEffect 中的依賴項,它們在 useEffect 中沒有使用。 這將導致 useEffect 在每次更新latitudelongitude時也會觸發。 我的第一個建議是將它們從依賴項數組中刪除。

改變這個:

useEffect(() => {
    // open popup if it exists
    if (selectedMarker) {
      markerRef.current[selectedMarker]?.openPopup();
    }
  }, [selectedMarker, latitude, longitude]);

其次,要做的一件好事是確保彈出窗口尚未打開,這可以通過

對此:

useEffect(() => {
    // open popup if it exists
    if (selectedMarker && markerRef.current[selectedMarker]?isPopupOpen()) {
      markerRef.current[selectedMarker]?.openPopup();
    }
  }, [selectedMarker, markerRef]);

作為我使用 react-leaflet 的經驗的旁注,不必要的重新渲染會導致一些視覺刺激,例如閃爍。 您應該努力減少重新渲染的數量。 這可以使用useMemouseCallback掛鈎來完成。 通常傳遞作為函數的道具,arrays 或 object 可能會導致重新渲染,即使它們是相同的。

據我所知,您的代碼嵌套很深,看起來您在其他組件中定義了組件。 嘗試打破你的代碼來清除和清理組件,而不是將道具傳遞給它們,你仍然可以將它們全部放在同一個文件中。

使用 eslint 和 typescript 也可以幫助您找到代碼問題。

比起@Disco 的回答。

我設法解決了這個問題:

  const StaticPointMarkers = (markers) => {
return useMemo(() => markers.map((point, i) => <SingleMarker key={'marker-' + i} point={point} />), [markers])};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM