提出問題
根據座標對象,查詢對應的地址詳情,代碼實現如下:
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渲染,coordinate對useEffect都是一個不同的值,也就是導致重複輸出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</>;
}