提出问题

根据坐标对象,查询对应的地址详情,代码实现如下:

const useAddress = (coordinate) => {
  const [address, setAddress] = useState(null);

  console.log('coming');

  useEffect(() => {
    const fetchData = async () => {
      const res = await getAddress(coordinate);
      if (res) {
        setAddress(res);
      }
    };

    if (coordinate) {
      fetchData();
    }
  }, [coordinate]);

  return address;
};

function App() {
  const coordinate = {
    longitude: 1.1,
    latitude: 1.1,
  };

  const res = useAddress(coordinate);

  return <>{JSON.stringify(res)}</>;
}

上面的代码看起来没问题,但是,当我们运行起来的时候,会发现控制台在一直不停地输出coming和发起请求。

分析问题

根据useEffect的用法我们可知,只有当依赖发生变化的时候,才会执行内部的代码,所以问题在其依赖的coordinate上。

但我们的coordinate确实是没有变化,固定的值,每次都是这个,那问题还有可能出在哪里呢?对的,是引用!

coordinate的内容虽然是一样,但是他是个引用变量,每App渲染,就会创建一个内容一样,但是引用不一样的coordinate,也就是说每次App渲染,coordinateuseEffect都是一个不同的值,也就是导致重复输出coming和发起请求的元凶了。

解决问题

知道了原因,解决思路就清晰了,目标就是让coordinate的引用稳定下来。

方案1:将coordinate提到组件外部:
脱离了组件的渲染,coordinate的引用就不会发生变化。

const coordinate = {
  longitude: 1.1,
  latitude: 1.1,
};

function App() {
  const res = useAddress(coordinate);

  return <>{JSON.stringify(res)}</>;
}

方案2:使用useMemo
useMemo能够缓存数据的引用,即便是组件重新渲染了。

function App() {
  const coordinate = useMemo(
    () => ({
      longitude: 1.1,
      latitude: 1.1,
    }),
    []
  );

  useAddress(coordinate);

  return <>App</>;
}

方案3:将依赖修改为原始类型:
知道原因是引用变化导致的,那我们改用原始类型,也就是number string boolean这类非引用类型,因为他们每次渲染虽然重新创建了,他们的值是一样的,所以就不会导致useEffect非预期的认为依赖变化的问题。

const useAddress = (coordinate) => {
  const [address, setAddress] = useState(null);

  console.log('coming');

  useEffect(() => {
    const fetchData = async () => {
      const res = await getAddress(coordinate);
      if (res) {
        setAddress(res);
      }
    };

    if (coordinate) {
      fetchData();
    }

  // 修改这里的依赖为原始类型
  }, [coordinate.longitude, coordinate.latitude]);

  return address;
};

function App() {
  const coordinate = {
    longitude: 1.1,
    latitude: 1.1,
  };
  useAddress(coordinate);

  return <>App</>;
}

热饭班长
3.7k 声望434 粉丝

先去做,做出一坨狗屎,再改进。