React中点击地图标记展开MaterialUI Accordion的实现方法
点击Marker展开对应Accordion的实现方案
需求描述
我有一个带5个Marker的地图,侧边是包含5个元素的MaterialUI Accordion,每个Marker和Accordion通过loc.properties.id一一对应,都是用map()函数生成的。需要实现点击Marker时自动展开对应的Accordion功能。
现有代码:
Markers组件
function Markers() { const map = useMap(); return ( loc.map(loc => { return ( <Marker icon={locationMarker} key={loc.properties.id} position={[loc.properties.y, loc.properties.x]}> </Marker> ) }) ) } export default Markers
LocationCard组件
function LocationCard() { return ( <Container> {loc.map(loc => ( <Accordion key={loc.properties.id}> <AccordionSummary> <Typography> {loc.properties.title} </Typography> </AccordionSummary> <AccordionDetails> <Typography> { loc.properties.description } </Typography> </AccordionDetails> </Accordion> ))} </Container> ); } export default LocationCard
实现方案
核心思路是状态提升:把控制Accordion展开的状态放到两个组件的共同父组件中,通过回调函数实现Marker点击和Accordion状态的联动。
1. 创建父组件管理状态
新建父组件(比如MapWithAccordions),用useState跟踪当前展开的Accordion ID,并提供状态切换的函数:
import { useState } from 'react'; import Markers from './Markers'; import LocationCard from './LocationCard'; function MapWithAccordions() { // 初始无展开项,用null表示 const [expandedId, setExpandedId] = useState(null); // 切换展开状态:点击同一项则关闭,否则展开 const toggleExpanded = (id) => { setExpandedId(prevId => prevId === id ? null : id); }; return ( <div style={{ display: 'flex' }}> {/* 给Markers传递点击回调 */} <Markers onMarkerClick={toggleExpanded} /> {/* 给LocationCard传递当前展开ID和切换函数 */} <LocationCard expandedId={expandedId} toggleExpanded={toggleExpanded} /> </div> ); } export default MapWithAccordions;
2. 修改Markers组件添加点击事件
接收父组件传递的onMarkerClick回调,给每个Marker绑定点击事件,触发时传递对应ID:
function Markers({ onMarkerClick }) { const map = useMap(); return ( loc.map(loc => { const markerId = loc.properties.id; return ( <Marker icon={locationMarker} key={markerId} position={[loc.properties.y, loc.properties.x]} onClick={() => onMarkerClick(markerId)} // Marker点击触发状态切换 /> ) }) ) } export default Markers
3. 修改LocationCard组件控制展开状态
接收父组件的expandedId和toggleExpanded,通过expanded属性控制每个Accordion的展开状态,同时保留Accordion自身的点击切换功能:
import { Accordion, AccordionSummary, AccordionDetails, Typography, Container } from '@mui/material'; function LocationCard({ expandedId, toggleExpanded }) { return ( <Container> {loc.map(loc => { const accordionId = loc.properties.id; return ( <Accordion key={accordionId} expanded={expandedId === accordionId} // 受控展开状态 onChange={() => toggleExpanded(accordionId)} // 自身点击也触发状态切换 > <AccordionSummary> <Typography>{loc.properties.title}</Typography> </AccordionSummary> <AccordionDetails> <Typography>{loc.properties.description}</Typography> </AccordionDetails> </Accordion> ) })} </Container> ); } export default LocationCard
关键说明
- 状态共享:通过父组件统一管理展开状态,确保Marker和Accordion的状态同步。
- 受控组件:Accordion的
expanded属性由父组件的expandedId控制,避免状态分散导致的不一致。 - 双向联动:不仅Marker点击能切换Accordion,Accordion自身点击也能正常切换状态,逻辑统一。
内容的提问来源于stack exchange,提问作者Gatnik
相关产品推荐
相关产品推荐

