React.memo 无法使用功能组件和 google 地图
React.memo not working with functoinal components and google maps
我正在尝试在 React 应用程序中插入 google 地图。我宁愿不使用非官方图书馆(我发现那些缺乏文档的图书馆)并且我已经设法插入地图。
我的问题是每次父组件的状态改变时都会重新渲染地图;尽管更改的值与地图的需要完全无关。
经过一些研究(我是 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} height="95%">
<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;
每次当用户键入 phone 并且状态发生变化时调用 setPhone 时,都会重新呈现地图。有人可以向我解释为什么 React.memo 不起作用吗?我应该如何进行以避免重新渲染地图?
我觉得我的直觉是这个组件
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
包裹了其他所有东西。
我正在尝试在 React 应用程序中插入 google 地图。我宁愿不使用非官方图书馆(我发现那些缺乏文档的图书馆)并且我已经设法插入地图。
我的问题是每次父组件的状态改变时都会重新渲染地图;尽管更改的值与地图的需要完全无关。
经过一些研究(我是 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} height="95%">
<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;
每次当用户键入 phone 并且状态发生变化时调用 setPhone 时,都会重新呈现地图。有人可以向我解释为什么 React.memo 不起作用吗?我应该如何进行以避免重新渲染地图?
我觉得我的直觉是这个组件
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
包裹了其他所有东西。