[英]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 上打開了彈出窗口,但這會產生閃爍。
強制彈出窗口保持打開狀態的最佳方法是什么?
如果我理解你的問題,問題是當用戶位置改變時,選定的標記彈出窗口閃爍?
一般來說,只要不再次創建標記,彈出窗口就應該能夠保持打開狀態。
我可以看到您將latitude
和longitude
作為 useEffect 中的依賴項,它們在 useEffect 中沒有使用。 這將導致 useEffect 在每次更新latitude
和longitude
時也會觸發。 我的第一個建議是將它們從依賴項數組中刪除。
改變這個:
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 的經驗的旁注,不必要的重新渲染會導致一些視覺刺激,例如閃爍。 您應該努力減少重新渲染的數量。 這可以使用useMemo
和useCallback
掛鈎來完成。 通常傳遞作為函數的道具,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.