如何在props变化时强制react-map-gl的Map组件重新渲染
问题:react-map-gl上传GeoJSON后需平移地图才显示
我在项目中使用react-map-gl的Map组件,通过props传入props.displayUploadedFiles(一个GeoJSON对象数组),想借助Source和Layer组件在地图上展示这些GeoJSON数据。但上传文件后,必须平移地图触发重渲染才能看到数据。
已经确认文件上传成功且是标准GeoJSON格式,传入Map组件的props也正常更新,但依赖该props的钩子要等到平移地图才会触发。
代码示例
// App.tsx export default function App(): JSX.Element { const [files, setFiles] = useState<any[]>([]); const setFilesState = (filesUploaded: File[]) => { const jsons = kmlToGeoJson(filesUploaded); setFiles(jsons); }; return ( <> <MyMap displayUploadedFiles={files} ... /> <MyFileUploader fileStateUpdater={setFilesState} /> </> ); } // MyMap.tsx function MapboxMap(props: MapboxMapProps): JSX.Element { const geoJsons = useMemo(() => (props.displayUploadedFiles.map((file) => { const id = JSON.stringify(file.features); return ( <Source key={id} type="geojson" data={file}> <Layer key={id} {...pointLayer} /> </Source> ); })), [JSON.stringify(props.displayUploadedFiles)]); // 仅在平移地图后才会执行上传后的更新 return ( <Map mapboxAccessToken={mapboxToken} ...> {geoJsons} ... </Map> ); } function MyMap(props: StyledMapProps): JSX.Element { return ( <div id={sx.styledMap}> <MapboxMap ... displayUploadedFiles={props.displayUploadedFiles} /> ... ); } export default MyMap;
已尝试的方法
- 参照类组件的解决方案使用
useEffect,问题依旧 - 用
useMemo并将依赖设为JSON.stringify(props.displayUploadedFiles),试图解决数组引用未变导致不重渲染的问题,但无效 - 调整
MyFileUploader的位置,将逻辑移入MyMap或MapboxMap内,props更新正常,但钩子仍需平移地图才触发
解决方案
1. 修复useMemo依赖项,确保引用更新
不要用JSON.stringify作为依赖,直接使用props.displayUploadedFiles。如果kmlToGeoJson返回的对象存在引用复用的情况,转换后生成新对象确保引用更新:
// App.tsx中修改setFilesState const setFilesState = (filesUploaded: File[]) => { const jsons = kmlToGeoJson(filesUploaded).map(item => ({...item})); // 生成新对象,强制更新引用 setFiles(jsons); }; // MapboxMap中修改useMemo依赖 const geoJsons = useMemo(() => props.displayUploadedFiles.map((file) => { const id = file.features?.[0]?.properties?.id || `${Date.now()}-${Math.random()}`; // 用更稳定的key return ( <Source key={id} type="geojson" data={file}> <Layer key={id} {...pointLayer} /> </Source> ); }), [props.displayUploadedFiles]);
2. 手动触发地图刷新
通过ref获取Map实例,在displayUploadedFiles变化时调用refresh方法强制刷新:
function MapboxMap(props: MapboxMapProps): JSX.Element { const mapRef = useRef<Map>(null); // 当props变化时强制刷新地图 useEffect(() => { mapRef.current?.refresh(); }, [props.displayUploadedFiles]); const geoJsons = useMemo(() => props.displayUploadedFiles.map((file) => { const id = file.features?.[0]?.properties?.id || `${Date.now()}-${Math.random()}`; return ( <Source key={id} type="geojson" data={file}> <Layer key={id} {...pointLayer} /> </Source> ); }), [props.displayUploadedFiles]); return ( <Map ref={mapRef} mapboxAccessToken={mapboxToken} ...> {geoJsons} ... </Map> ); }
3. 单独包裹GeoJSON数据确保可追踪
如果GeoJSON数据结构复杂,react-map-gl可能无法检测到data的变化,可将每个GeoJSON对象用useMemo单独包裹:
// MapboxMap中修改渲染逻辑 const geoJsons = props.displayUploadedFiles.map((file) => { // 单独包裹data,确保变化时Source能感知 const geoData = useMemo(() => file, [JSON.stringify(file)]); const id = useMemo(() => JSON.stringify(file.features), [file.features]); return ( <Source key={id} type="geojson" data={geoData}> <Layer key={id} {...pointLayer} /> </Source> ); });
内容的提问来源于stack exchange,提问作者artificialask
相关产品推荐
相关产品推荐

