【问题标题】:How to resolve "React Hook useEffect has a missing dependency: 'currentPosition'"如何解决“React Hook useEffect 缺少依赖项:'currentPosition'”
【发布时间】:2021-06-24 10:56:20
【问题描述】:

当我在useEffect 依赖数组中包含currentPosition 或删除它时,代码会变成无限循环。为什么? 我对 map 有同样的问题,但是当我将 map 放在依赖数组中时就可以了。

import { useState, useEffect } from "react";

import { useMap } from "react-leaflet";
import L from "leaflet";

import icon from "./../constants/userIcon";

const UserMarker = () => {
  const map = useMap();
  const [currentPosition, setCurrentPosition] = useState([
    48.856614,
    2.3522219,
  ]);

  useEffect(() => {
    if (navigator.geolocation) {
      let latlng = currentPosition;
      const marker = L.marker(latlng, { icon })
        .addTo(map)
        .bindPopup("Vous êtes ici.");
      map.panTo(latlng);

      navigator.geolocation.getCurrentPosition(function (position) {
        const pos = [position.coords.latitude, position.coords.longitude];
        setCurrentPosition(pos);
        marker.setLatLng(pos);
        map.panTo(pos);
      });
    } else {
      alert("Problème lors de la géolocalisation.");
    }
  }, [map]);

  return null;
};

export default UserMarker;

【问题讨论】:

  • 什么是currentPosition?它不在您提供的代码中。
  • 当你在useEffect的数组第二个参数中放入一个变量,每次变量变化时re​​act都会调用该函数。每次调用 useEffect 时,您都在更改 map 变量,导致它被一遍又一遍地调用。您可以添加一个条件来检查它是否具有您期望的数据,如果有,则不更新map。但是,文档显示的示例与您的示例大不相同,也许尝试react-leaflet 方式?
  • 对不起,我复制了错误的代码!

标签: reactjs leaflet react-leaflet esri-leaflet-geocoder react-leaflet-search


【解决方案1】:

来自 DCTID 的评论解释了在 useEffect 钩子中包含状态会创建无限循环的原因。

您需要确保不会发生这种情况!你有两个选择:

  1. 添加忽略评论并保持原样

  2. 创建一个额外的冗余变量来存储变量currentPosition的当前值,并且只有在值实际发生变化时才执行函数

第二种方法的实现:

let currentPosition_store = [48.856614, 2.3522219];

useEffect(() => {
    if (!hasCurrentPositionChanged()) {
        return;
    }

    currentPosition_store = currentPosition;

    // remaining function

    function hasCurrentPositionChanged() {
        if (currentPosition[0] === currentPosition_store[0] &&
            currentPosition[1] === currentPosition_store[1]
        ) {
            return false;
        }
        
        return true;
    }
}, [map, currentPosition]);

【讨论】:

    【解决方案2】:

    谢谢,我已经解决了这个冲突:

    import { useEffect } from "react";
    
    import { useMap } from "react-leaflet";
    import L from "leaflet";
    
    import icon from "./../constants/userIcon";
    
    const UserMarker = () => {
      const map = useMap();
    
      useEffect(() => {
        const marker = L.marker;
        if (navigator.geolocation) {
          navigator.geolocation.getCurrentPosition(function (position) {
            const latlng = [position.coords.latitude, position.coords.longitude];
            marker(latlng, { icon })
              .setLatLng(latlng)
              .addTo(map)
              .bindPopup("Vous êtes ici.");
            map.panTo(latlng);
          });
        } else {
          alert("Problème lors de la géolocalisation.");
        }
      }, [map]);
    
      return null;
    };
    
    export default UserMarker;
    

    【讨论】:

      【解决方案3】:

      如果 currentPosition 在依赖数组中,则出现无限循环的原因:

      const [currentPosition, setCurrentPosition] = useState([
          48.856614,
          2.3522219,
        ]);
      

      您最初有一个 currentPosition 的值,然后您在 useEffect 内部进行更改,这会导致您的组件重新渲染,并且这种情况会无限发生。你不应该把它添加到依赖数组中。

      您收到“缺少依赖项警告”的原因是,如果您在 useEffect 中使用的任何变量在该组件内定义或作为道具传递给组件,则必须将其添加到依赖项数组中,否则反应警告你。这就是为什么您应该将 map 添加到数组中的原因,并且由于您没有在 useEffect 中更改它,因此不会导致重新渲染。

      在这种情况下,您必须通过添加以下内容来告诉 es-lint 不要向我显示该警告://eslint-disable-next-line react-hooks/exhaustive-deps,因为您知道自己在做什么:

      useEffect(() => {
         if (navigator.geolocation) {
            let latlng = currentPosition;
            const marker = L.marker(latlng, { icon })
              .addTo(map)
              .bindPopup("Vous êtes ici.");
            map.panTo(latlng);
      
            navigator.geolocation.getCurrentPosition(function (position) {
              const pos = [position.coords.latitude, position.coords.longitude];
              setCurrentPosition(pos);
              marker.setLatLng(pos);
              map.panTo(pos);
            });
          } else {
            alert("Problème lors de la géolocalisation.");
          }
          // eslint-disable-next-line react-hooks/exhaustive-deps
          }, [map]);
       
      

      该注释将关闭对该行代码的依赖性检查。

      【讨论】:

        【解决方案4】:

        为了便于理解,我会先指出原因,然后再给出解决方案。

        1. 为什么?我对 map 也有同样的问题,但是当我将 map 放在依赖数组中时就可以了。

        Answer: 原因是 useEffect 是根据它的依赖关系重新运行的。 useEffect 在组件渲染时首次运行 -> 组件重新渲染(因为它的 props 发生变化...) -> useEffect 将 shallow 比较并在其依赖项发生变化时重新运行。

        • 在你的情况下,map Leaflet Map 我敢打赌,如果你的组件只是重新渲染 -> 当你重新渲染组件时 -> map (Leaflet Map),react-leaflet 将返回相同的 Map 实例(相同的引用)实例)不要改变 -> useEffect 不重新运行 -> 无限循环不会发生。
        • currentPosition 是您的本地状态,您在 useEffect 中更新它 setCurrentPosition(pos); -> 组件重新渲染 -> currentPosition 依赖项更改(currentPosition 在浅比较中不同)-> useEffect 重新运行 -> setCurrentPosition(pos);使组件重新渲染 -> 无限循环
        1. 解决方案:

        有一些解决方案:

        • 通过在依赖项行上方添加// eslint-disable-next-line exhaustive-deps 禁用 lint 规则。但这根本不推荐。通过这样做,我们打破了 useEffect 的工作方式。
        • 拆分你的useEffect:

        import { useState, useEffect } from "react";
        
        import { useMap } from "react-leaflet";
        import L from "leaflet";
        
        import icon from "./../constants/userIcon";
        
        const UserMarker = () => {
          const map = useMap();
          const [currentPosition, setCurrentPosition] = useState([
            48.856614,
            2.3522219,
          ]);
          
          // They are independent logic so we can split it yo
          useEffect(() => {
            if (navigator.geolocation) {
              let latlng = currentPosition;
              const marker = L.marker(latlng, { icon })
                .addTo(map)
                .bindPopup("Vous êtes ici.");
              map.panTo(latlng);
            } else {
              alert("Problème lors de la géolocalisation.");
            }
          }, [map, currentPosition]);
        
          useEffect(() => {
            if (navigator.geolocation) {
              navigator.geolocation.getCurrentPosition(function (position) {
                const pos = [position.coords.latitude, position.coords.longitude];
                setCurrentPosition(pos);
                marker.setLatLng(pos);
                map.panTo(pos);
              });
            }
          }, [map]);
        
          return null;
        };
        
        export default UserMarker;

        Dan 有一篇关于 useEffect 的精彩文章,值得一看:https://overreacted.io/a-complete-guide-to-useeffect/#dont-lie-to-react-about-dependencies

        【讨论】:

        • 您可能需要在第二个 useEffect 中包含 setCurrentPosition 作为依赖项,以避免另一个 eslint 警告。不会有问题,因为setState 是稳定的函数。
        • @buzatto From React docs,你不需要。 React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
        • 这篇文章感谢您的帮助。
        • @gcbox999 如果这有助于回答您的问题并解决您的问题,请考虑投票或接受我的回答。
        猜你喜欢
        • 2020-03-27
        • 2021-02-23
        • 2019-10-24
        • 2020-10-26
        • 2021-05-17
        • 2020-03-07
        • 2020-02-25
        • 2020-06-11
        • 2020-03-30
        相关资源
        最近更新 更多