Javascript React Hooks—调用函数的最佳位置,该函数取决于“a”中的状态;“一次”;使用效果?

Javascript React Hooks—调用函数的最佳位置,该函数取决于“a”中的状态;“一次”;使用效果?,javascript,reactjs,react-hooks,use-effect,Javascript,Reactjs,React Hooks,Use Effect,我正在尝试使用React钩子实现googlemaps,而不使用任何第三方库。我已经编写了代码,允许生成脚本,使用如下所示的url获取google地图。但是我不知道调用这个脚本生成函数的正确方法是什么(createScriptLoadMap()),因为在useffect中调用它,只调用一次似乎会给我一个错误: 我想知道为什么会出现这个错误,以及如何解决它。 这是一个stackblitz链接,我已经尽了最大努力为我的情况编写了一个最小的代码: 编辑:我也在执行来自useffect的get请求,因此

我正在尝试使用React钩子实现googlemaps,而不使用任何第三方库。我已经编写了代码,允许生成脚本,使用如下所示的url获取google地图。但是我不知道调用这个脚本生成函数的正确方法是什么(
createScriptLoadMap()
),因为在
useffect
中调用它,只调用一次似乎会给我一个错误:

我想知道为什么会出现这个错误,以及如何解决它。 这是一个stackblitz链接,我已经尽了最大努力为我的情况编写了一个最小的代码:

编辑:我也在执行来自
useffect
的get请求,因此它必须是“一次”
useffect
,但我也必须调用
createScriptLoadMap()
一次,并且此函数取决于
useffect
设置的状态,它不会立即执行

是否有任何方法可以使get请求发生,设置
mapCenter
,然后执行
createScriptLoadMap()

钩子在这种情况下帮不了你。即使您从useffect返回并从
头部删除
元素
。因为这个脚本在
head
中创建了大量的其他元素(比如样式),仅仅删除一个元素是没有帮助的

我要说的是,不引入反应就做事情是不对的,但是如果你需要的话,你可以这样做。例如,在脚本标记中添加一些内容,并在下次选中此项,然后跳过
createScriptLoadMap
。像这样:

const createScriptLoadMap=()=>{
var gScript=document.querySelector(“脚本[数据设置]”);
var isSetted=gScript&&gScript.getAttribute(“数据设置”);
如果(!已设置){
var index=document.getElementsByTagName(“脚本”)[0];
var script=document.createElement(“脚本”);
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(“数据设置”,true);
index.parentNode.insertBefore(脚本,索引);
window.initMap=initMap;
}否则{
//我不确定这是否会引起问题
//但如果没有它,stackblitz就什么也做不成
initMap();
}
};

您的
useffect
钩子只执行一次。您收到该消息的原因很可能是热重新加载和重新安装
Map
组件

为了避免这种情况,只需使用正在设置的window属性
window.initMap
检查是否加载了映射脚本:

const createScriptLoadMap=()=>{
如果(!window.initMap){
var index=window.document.getElementsByTagName(“脚本”)[0];
var script=window.document.createElement(“脚本”);
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(脚本,索引);
window.initMap=true;
}
};
编辑

至于如何在获取一些数据后将地图居中,您还应该使用hook和
useffect
,因为您已经有了
mapCenter
状态对象。首先使用
script.load
查看脚本何时加载。调用
initMap
无论是否加载脚本,都可以获取
google.maps.Map
对象,并使用state将其保存以备以后使用,如果脚本已加载,则只需分配映射对象:

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
对象分配给out DOM
#Map
元素(如果之前已经分配了元素,则不会创建新的映射):

这将创建一个
google.aps.Map
对象,并使用
setMap
状态将其保存以供以后使用。现在,无论何时要更新地图的中心(或任何其他地图属性/方法),只需将
useffect
挂钩设置为
mapCenter
状态对象,然后使用
map
对象更改地图:

const [mapCenter, setMapCenter] = useState({});
const [fetchedData, setFetchedData] = useState({});
const [map, setMap] = useState();

useEffect(() => {
  createScriptLoadMap();
}, []);

useEffect(() => {
  if(map) {
    map.setCenter(mapCenter)
  }
}, [mapCenter]);

检查我的分叉和更新。您将看到地图从雅典开始,延迟2秒后(假设我们有一个异步Axios调用),它将地图集中到纽约布鲁克林。(我创建了我的Google Maps API密钥用于演示,我将在您的响应后删除)。

这是因为您的
createScriptLoadMap()
正在多次调用,一个快速的方法是创建一个初始值为false的变量,然后在函数
createScriptLoadMap()
内调用
createScriptLoadMap()
,将变量设置为true。在
useffect()
中,检查变量是否为false,然后调用
createScriptLoadMap()
。另一种方法是将map组件定义为类组件。谢谢您的回答。这确实是一个简单的解决方案,但只解决了我问题的一半,因为我需要get请求的响应(我现在已经对其进行了注释)才能在initMap()中使用它。我试着在第14行和第23行的stackblitz中解释这一点。由于useEffect只发生一次,因此它会使用“createScriptLoadMap()”加载映射一次,但由于异步性,不会设置“mapCenter”(第14行)。有什么解决办法吗?如果它是基于类的,我想我可以在setState的回调中使用它,但是我被钩子绊住了。你可以用钩子创建所有的逻辑。钩子是如此强大。只需使用
script.load
查看Google地图脚本何时完成加载,然后使用state保存地图对象。检查我的最新答案和我的答案。啊,非常清楚!因此,在axios请求发生后,我们设置了对某个内容的响应(比如说中间)
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);
};
const [mapCenter, setMapCenter] = useState({});
const [fetchedData, setFetchedData] = useState({});
const [map, setMap] = useState();

useEffect(() => {
  createScriptLoadMap();
}, []);

useEffect(() => {
  if(map) {
    map.setCenter(mapCenter)
  }
}, [mapCenter]);