博客 / 詳情

返回

當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]);

  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</>;
}
user avatar kevin_5d8582b6a85cd 頭像 _bleach 頭像 nxmin 頭像
3 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.