如何通过useContext、useRef连接React组件 正确编写Leaflet相关自定义Hook
问题解答
一、当前实现的正确性
你当前的基础运行逻辑是可行的,但存在3个可以优化的问题点:
- Map组件内同时写了监听
generateIso的useEffect,又调用了自定义Hook,存在重复逻辑 - 你编写的自定义Hook示例直接使用了未传入的
generateIso、setGenerateIso变量,运行时会报未定义错误 - 给自定义Hook传参时传了
mapRef.current而不是ref本身,如果map实例还未初始化完成时传值会是null,后续map实例更新也不会触发Hook重新执行
二、简化方案 & 自定义Hook正确写法
你可以把Context消费、地图操作相关的逻辑全部封装到自定义Hook内,避免每个地图组件重复编写监听逻辑。
1. 改造自定义Hook
import { useContext, useEffect } from "react"; import { ToolContext } from "../../../context/ToolContext"; // 入参直接传ref对象,不要传ref.current export const useGenerateIso = (mapRef) => { // Hook内部直接消费Context,不需要父组件额外传值 const { generateIso, setGenerateIso } = useContext(ToolContext); useEffect(() => { // 增加边界判断:map实例存在、触发标识为true时才执行逻辑 if (!mapRef.current || !generateIso) return; // 此处写入你的等时线生成/移除逻辑 // 示例:L.marker([0,0]).addTo(mapRef.current) // 执行完成后重置触发状态 setGenerateIso(false); }, [generateIso, mapRef, setGenerateIso]); };
2. 简化Map组件
改造后的Map组件可以删掉重复的useEffect监听逻辑,仅调用自定义Hook即可:
import React, { useRef, useEffect } from "react"; import L from "leaflet"; import { useGenerateIso } from "./useGenerateIso"; // 引入自定义Hook export default function Map(props) { const mapRef = useRef(null); // 调用自定义Hook,直接传入ref对象 useGenerateIso(mapRef); useEffect(() => { mapRef.current = L.map(props.id, { editable: true, zoomControl: false, contextmenu: true, maxZoom: 20, minZoom: 6, doubleClickZoom: false, contextmenuWidth: 120, }) // 增加地图销毁逻辑,避免内存泄漏 return () => mapRef.current?.remove(); }, [props.id]); return ( <div ref={mapRef} id={props.id} style={{ position: "absolute", width: "100%", height: "calc(100vh - 64px)", }} /> ); }
3. 多逻辑复用扩展
如果有大量类似的触发逻辑,你可以按功能拆分不同的自定义Hook,比如useDrawPolygon、useClearLayers等,每个Hook仅负责单一功能,在Map组件内按需引入调用即可,不需要单独维护大量的状态监听逻辑。
三、可选优化方案
如果你觉得Context内维护大量布尔触发量太繁琐,也可以将Context的状态改成事件队列形式,每次按钮点击往队列里push一个事件对象,比如{type: 'GENERATE_ISO', payload: {}},自定义Hook监听队列变化,匹配对应事件类型执行逻辑,这种方案更适合存在大量地图操作指令的场景。
内容的提问来源于stack exchange,提问作者pawaelus
相关产品推荐
相关产品推荐

