React.memo 不适用于功能组件和谷歌地图
Posted
技术标签:
【中文标题】React.memo 不适用于功能组件和谷歌地图【英文标题】:React.memo not working with functoinal components and google maps 【发布时间】:2021-10-12 06:07:33 【问题描述】:我正在尝试在 React 应用程序中插入谷歌地图。我宁愿不使用非官方的库(我发现那些缺少文档的库),而且我已经设法插入了地图。
我的问题是每次父组件的状态改变时都会重新渲染地图;尽管改变的值与地图需要的完全无关。
经过一番研究(我是 React 新手),我发现了 React.memo() HOC,它应该可以防止子组件在其 props 未更改时重新渲染。但是由于某种原因,我无法让它正常工作。当我将地图插入没有道具的组件中时发生的事件,父状态的任何更改都会导致重新渲染地图。
这里是父组件:
const CompanyDepotsPopup = () =>
const classes = useStyles();
const dispatch = useDispatch();
const open = useSelector((state) => selectIsDepotsPopupOpen(state));
const company = useSelector((state) => selectSelectedCompany(state));
const depotsStatus = useSelector((state) => selectDepotsStatus(state));
const t = useTranslation();
const [value, setValue] = useState(0);
const [phone, setPhone] = useState("");
const handleChange = (event, newValue) =>
setValue(newValue);
;
const closeModal = () =>
dispatch(toggleDepotsPopup());
useEffect(() =>
if (company)
dispatch(depotsListed(companyId: company.id));
, [company])
if (!company) return <></>;
if (depotsStatus === "loading")
return <CenteredLoader/>
function TabPanel(props)
const children, value, index = props;
return (
<div
hidden=value !== index
style=height: "100%"
>
value === index && (
<Box boxShadow=3 mt=1 ml=2 mr=2 height="100%">
children
</Box>
)
</div>
);
return (
<Dialog fullWidth=true open=open
aria-labelledby="company-dialog-popup">
<DialogTitle >
company.name
</DialogTitle>
<DialogContent style=padding: 0, margin: 0>
<Divider/>
<Box mr=0 ml=0 mt=0 p=0 >
<div >
<AppBar position="static">
<Tabs value=value onChange=handleChange aria-label="depots tabs" centered>
<Tab label=t("Company's depots list")/>
<Tab label=t("Add new depot")/>
</Tabs>
</AppBar>
<TabPanel value=value index=0>
<DepotsList/>
</TabPanel>
<TabPanel value=value index=1>
<Paper>
<Grid container spacing=2>
<Grid item xs=12 sm=12 md=12 lg=12>
<TextField
onChange=(event) => setPhone(event.target.value)
id="phone"
label=t("Phone")
type="text"
fullWidth
value=phone
/>
</Grid>
<Grid item xs=12 sm=12 md=12 lg=12>
<div style=height: "250px", display: "flex", "flexDirection": "column">
<MyMap
id="myMap"
/>
</div>
</Grid>
<Grid item xs=12 sm=12 md=12 lg=12 align="center">
<Button variant="outlined">
t("Save")
</Button>
</Grid>
</Grid>
</Paper>
</TabPanel>
</div>
</Box>
</DialogContent>
<DialogActions style=marginTop: "20px">
<Button
variant="outlined"
onClick=closeModal
color="secondary"
>
Done
</Button>
</DialogActions>
</Dialog>
)
这是地图组件:
import React, useEffect from "react";
const Map = (id) =>
const onScriptLoad = () =>
const map = new window.google.maps.Map(
document.getElementById(id),
center: lat: 41.0082, lng: 28.9784,
zoom: 8
);
const marker = new window.google.maps.Marker(
position: lat: 41.0082, lng: 28.9784,
map: map,
title: 'Hello Istanbul!'
);
useEffect(() =>
if (!window.google)
const s = document.createElement("script");
s.type = "text/javascript";
s.src = "https://maps.google.com/maps/api/js?key=''"
const x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
s.addEventListener('load', e =>
onScriptLoad();
)
else
onScriptLoad();
, []);
return (
<div style=width: "100%", height: "100%" id=id/>
);
const MyMap = React.memo(Map);
export default MyMap;
当用户键入电话并且状态发生变化时,每次调用 setPhone 时,都会重新渲染地图。有人可以向我解释为什么 React.memo 不起作用以及我应该如何进行以避免重新渲染地图?
【问题讨论】:
这很奇怪,我们在Map
的第一行下面放一个console.log
,在useEffect
的第一行下面放一个。如果两个打印输出都显示多个渲染,那就证实了你所说的。
我意识到我无法真正回答你的问题,但我创建了一个codesandbox,也许可以帮助你更好地理解React.memo
【参考方案1】:
我觉得我的直觉是这个组件
function TabPanel(props)
const children, value, index = props;
return (
<div
hidden=value !== index
style=height: "100%"
>
value === index && (
<Box boxShadow=3 mt=1 ml=2 mr=2 height="100%">
children
</Box>
)
</div>
);
这是在组件内部定义的,因此该组件的实例在任何状态更改后都会不断变化。为了防止它,把它移到组件之外,像这样
function TabPanel()
function CompanyDepotsPopup()
代替
function CompanyDepotsPopup()
function TabPanel()
原因也是你的TabPanel
包装了其他所有内容。
【讨论】:
你说得对,就是这样。这当然是有道理的,只是我从未想过。谢谢! 不客气,我也摸不着头脑,因为您的大部分代码都不会导致任何重新渲染,尤其是 MyMap :) 很高兴这有帮助。以上是关于React.memo 不适用于功能组件和谷歌地图的主要内容,如果未能解决你的问题,请参考以下文章
navigator.Geolocation GetCurrentpositon 不适用于 Chrome 和 Firefox
谷歌地图不适用于 ionic cordova run android