React Hooks - 调用依赖于 "one time" useEffect 状态的函数的最佳位置?
React Hooks- Best place to call a function that depends on state from a "one time" useEffect?
我正在尝试使用 React Hooks 实现 google 地图,而不使用任何类型的第三方库。我编写了允许生成脚本的代码,该脚本使用 url 获取 google 地图,如下所示。但是我不知道调用这个脚本生成函数的正确方法是什么(createScriptLoadMap()
在我们的例子中),因为在 useEffect
中调用它只被调用一次似乎给我一个错误:
You have included the Google Maps JavaScript API multiple times on
this page. This may cause unexpected errors.
我想知道为什么会出现此错误以及如何解决。
这是一个 stackblitz link,我已经尽力为我的情况制作了最少的代码:
https://stackblitz.com/edit/react-hsl4dn?file=Hello.js
编辑:我也在做一个来自 useEffect
的获取请求,所以它必须是 "one time" useEffect
,但我必须调用 createScriptLoadMap()
一次,此功能取决于 useEffect
设置的状态,这不会立即执行!
有什么方法可以让 get 请求发生,设置 mapCenter
然后执行 createScriptLoadMap()
?
https://stackblitz.com/edit/react-ohtdra?file=Hello.js
Hooks 在这种情况下帮不了你。即使您从 useEffect 中 return clean-up function 并从 head
中删除 element
。因为此脚本在 head
(如样式) 中创建了大量其他元素,仅删除一个元素无济于事。
我需要说的是,不引入 React 就做事是不对的,但如果你需要的话,你可以。例如。在脚本标签中添加一些内容,下次检查并跳过 createScriptLoadMap
。像这样:
const createScriptLoadMap = () => {
var gScript = document.querySelector("script[data-setted]");
var isSetted = gScript && gScript.getAttribute("data-setted");
if (!isSetted) {
var index = document.getElementsByTagName("script")[0];
var script = document.createElement("script");
script.src =
"https://maps.googleapis.com/maps/api/js?key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU&libraries=drawing&callback=initMap";
script.async = true;
script.defer = true;
script.setAttribute("data-setted", true);
index.parentNode.insertBefore(script, index);
window.initMap = initMap;
} else {
// I'm not sure about this, can cause a problem
// but without it stackblitz doesn't render anything
initMap();
}
};
您的 useEffect
挂钩只执行一次。您收到该消息很可能是因为热重新加载和重新安装 Map
组件。
为了避免这种情况,只需使用您设置的 window 属性 window.initMap
检查地图脚本是否已加载:
const createScriptLoadMap = () => {
if (!window.initMap) {
var index = window.document.getElementsByTagName("script")[0];
var script = window.document.createElement("script");
script.src =
"https://maps.googleapis.com/maps/api/js?key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU&libraries=drawing&callback=initMap";
script.async = true;
script.defer = true;
index.parentNode.insertBefore(script, index);
window.initMap = true;
}
};
编辑
至于在获取一些数据后如何使地图居中,您还应该使用钩子和 useEffect
,因为您已经有一个 mapCenter
状态对象。首先使用 script.load
查看脚本何时加载。调用 initMap
是否加载脚本以获取 google.maps.Map
对象并使用状态保存它以备后用,否则如果脚本已经加载则只需分配地图对象:
const createScriptLoadMap = () => {
if (!window.initMap) {
var index = window.document.getElementsByTagName("script")[0];
var script = window.document.createElement("script");
script.src =
"https://maps.googleapis.com/maps/api/js?key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU&libraries=drawing&callback=initMap";
script.async = true;
script.defer = true;
script.onload = () => {
initMap();
};
index.parentNode.insertBefore(script, index);
window.initMap = true;
} else {
initMap();
}
};
然后改变 initMap
函数来初始化或分配一个 google.maps.Map
对象到输出 DOM #map
元素(不会创建新地图,如果我们之前已经分配了元素):
const initMap = () => {
let googleMap = new google.maps.Map(document.getElementById("map"), {
center: { lat: 37.978534, lng: 23.743830 }, //CURRENTLY STATIC
//center: {mapCenter} //ACTUAL
zoom: 14
});
// Save the map object created for later usage
setMap(googleMap);
// axios.get(`${BASE_URL_LOCAL}/outlet/list?page=1&limit=50`).then(res => {
// let center = averageGeolocation(res.data.data.outlets);
// console.log(props.mapCenter);
// setFetchedData(res);
// setMapCenter(center) //THIS CENTER IS REQUIRED IN LINE 22
// });
// Suppose get an Axios response after 2 seconds
setTimeout(() => {
// Center map at NY Brooklyn using state (effect for mapCenter will be triggered)
setMapCenter({ lat: 40.674282, lng: -73.943060 });
}, 2000);
};
这将创建一个 google.aps.Map
对象并使用 setMap
状态保存它供以后使用。现在每当你想更新地图的中心(或任何其他地图properties/methods),只需设置一个useEffect
挂钩到mapCenter
状态对象然后使用 map
对象来改变地图:
const [mapCenter, setMapCenter] = useState({});
const [fetchedData, setFetchedData] = useState({});
const [map, setMap] = useState();
useEffect(() => {
createScriptLoadMap();
}, []);
useEffect(() => {
if(map) {
map.setCenter(mapCenter)
}
}, [mapCenter]);
检查我的分叉和更新 stackblitz
。您会看到地图从雅典开始,在延迟 2 秒后(假设我们有一个异步 Axios 调用),它使地图以纽约布鲁克林为中心。 (我创建了我的 Google 地图 API 密钥用于演示,我会在您回复后删除)。
我正在尝试使用 React Hooks 实现 google 地图,而不使用任何类型的第三方库。我编写了允许生成脚本的代码,该脚本使用 url 获取 google 地图,如下所示。但是我不知道调用这个脚本生成函数的正确方法是什么(createScriptLoadMap()
在我们的例子中),因为在 useEffect
中调用它只被调用一次似乎给我一个错误:
You have included the Google Maps JavaScript API multiple times on this page. This may cause unexpected errors.
我想知道为什么会出现此错误以及如何解决。 这是一个 stackblitz link,我已经尽力为我的情况制作了最少的代码:
https://stackblitz.com/edit/react-hsl4dn?file=Hello.js
编辑:我也在做一个来自 useEffect
的获取请求,所以它必须是 "one time" useEffect
,但我必须调用 createScriptLoadMap()
一次,此功能取决于 useEffect
设置的状态,这不会立即执行!
有什么方法可以让 get 请求发生,设置 mapCenter
然后执行 createScriptLoadMap()
?
https://stackblitz.com/edit/react-ohtdra?file=Hello.js
Hooks 在这种情况下帮不了你。即使您从 useEffect 中 return clean-up function 并从 head
中删除 element
。因为此脚本在 head
(如样式) 中创建了大量其他元素,仅删除一个元素无济于事。
我需要说的是,不引入 React 就做事是不对的,但如果你需要的话,你可以。例如。在脚本标签中添加一些内容,下次检查并跳过 createScriptLoadMap
。像这样:
const createScriptLoadMap = () => {
var gScript = document.querySelector("script[data-setted]");
var isSetted = gScript && gScript.getAttribute("data-setted");
if (!isSetted) {
var index = document.getElementsByTagName("script")[0];
var script = document.createElement("script");
script.src =
"https://maps.googleapis.com/maps/api/js?key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU&libraries=drawing&callback=initMap";
script.async = true;
script.defer = true;
script.setAttribute("data-setted", true);
index.parentNode.insertBefore(script, index);
window.initMap = initMap;
} else {
// I'm not sure about this, can cause a problem
// but without it stackblitz doesn't render anything
initMap();
}
};
您的 useEffect
挂钩只执行一次。您收到该消息很可能是因为热重新加载和重新安装 Map
组件。
为了避免这种情况,只需使用您设置的 window 属性 window.initMap
检查地图脚本是否已加载:
const createScriptLoadMap = () => {
if (!window.initMap) {
var index = window.document.getElementsByTagName("script")[0];
var script = window.document.createElement("script");
script.src =
"https://maps.googleapis.com/maps/api/js?key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU&libraries=drawing&callback=initMap";
script.async = true;
script.defer = true;
index.parentNode.insertBefore(script, index);
window.initMap = true;
}
};
编辑
至于在获取一些数据后如何使地图居中,您还应该使用钩子和 useEffect
,因为您已经有一个 mapCenter
状态对象。首先使用 script.load
查看脚本何时加载。调用 initMap
是否加载脚本以获取 google.maps.Map
对象并使用状态保存它以备后用,否则如果脚本已经加载则只需分配地图对象:
const createScriptLoadMap = () => {
if (!window.initMap) {
var index = window.document.getElementsByTagName("script")[0];
var script = window.document.createElement("script");
script.src =
"https://maps.googleapis.com/maps/api/js?key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU&libraries=drawing&callback=initMap";
script.async = true;
script.defer = true;
script.onload = () => {
initMap();
};
index.parentNode.insertBefore(script, index);
window.initMap = true;
} else {
initMap();
}
};
然后改变 initMap
函数来初始化或分配一个 google.maps.Map
对象到输出 DOM #map
元素(不会创建新地图,如果我们之前已经分配了元素):
const initMap = () => {
let googleMap = new google.maps.Map(document.getElementById("map"), {
center: { lat: 37.978534, lng: 23.743830 }, //CURRENTLY STATIC
//center: {mapCenter} //ACTUAL
zoom: 14
});
// Save the map object created for later usage
setMap(googleMap);
// axios.get(`${BASE_URL_LOCAL}/outlet/list?page=1&limit=50`).then(res => {
// let center = averageGeolocation(res.data.data.outlets);
// console.log(props.mapCenter);
// setFetchedData(res);
// setMapCenter(center) //THIS CENTER IS REQUIRED IN LINE 22
// });
// Suppose get an Axios response after 2 seconds
setTimeout(() => {
// Center map at NY Brooklyn using state (effect for mapCenter will be triggered)
setMapCenter({ lat: 40.674282, lng: -73.943060 });
}, 2000);
};
这将创建一个 google.aps.Map
对象并使用 setMap
状态保存它供以后使用。现在每当你想更新地图的中心(或任何其他地图properties/methods),只需设置一个useEffect
挂钩到mapCenter
状态对象然后使用 map
对象来改变地图:
const [mapCenter, setMapCenter] = useState({});
const [fetchedData, setFetchedData] = useState({});
const [map, setMap] = useState();
useEffect(() => {
createScriptLoadMap();
}, []);
useEffect(() => {
if(map) {
map.setCenter(mapCenter)
}
}, [mapCenter]);
检查我的分叉和更新 stackblitz
。您会看到地图从雅典开始,在延迟 2 秒后(假设我们有一个异步 Axios 调用),它使地图以纽约布鲁克林为中心。 (我创建了我的 Google 地图 API 密钥用于演示,我会在您回复后删除)。