React Hooks-调用依赖于“一次” useEffect的状态的函数的最佳位置?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React Hooks-调用依赖于“一次” 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链接,在该链接中,我尽力为自己的情况编写了最少的代码:

https://stackblitz.com/edit/react-hsl4dn?file=Hello.js

编辑:我也在useEffect处进行获取请求,因此它必须是“一次” useEffect,但我也必须一次调用createScriptLoadMap(),此功能取决于useEffect设置的状态不会立即生效!

是否有发生获取请求,设置mapCenter然后执行createScriptLoadMap()的方式?

答案

您的useEffect钩子仅执行一次。您最有可能收到该消息,是因为热重新加载并重新安装了Map组件。

为了避免这种情况,只需使用您设置为window.initMap的window属性来检查是否加载了地图脚本:

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 get axios reequest 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状态保存以供以后使用。现在,只要您想更新地图的中心(或任何其他地图属性/方法),只需将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 Maps API密钥用于演示,在您回复后我将其删除)。

另一答案

stackblitz

在这种情况下,挂钩无法帮助您。即使从useEffect返回https://stackblitz.com/edit/react-ohtdra?file=Hello.js并从clean-up function中删除element。因为此脚本在head (类似样式)中创建了大量其他元素,仅删除一个元素将无济于事。

我需要说的是,在不引入React的情况下做事是不正确的,但是如果需要的话,可以。例如。在脚本标签中添加一些内容,然后再次选择此项,然后跳过head。像这样:

createScriptLoadMap

以上是关于React Hooks-调用依赖于“一次” useEffect的状态的函数的最佳位置?的主要内容,如果未能解决你的问题,请参考以下文章

React Hooks-useMemo篇

React Hooks: use modal

React Hooks-处理异步api调用

React Hooks用法大全

[React + GraphQL] Use useLazyQuery to manually execute a query with Apollo React Hooks

React Hooks 使用误区,驳官方文档