[英]Creating an array in the custom hook is re rendering value at every state change
我在 React Native 中創建自定義鈎子時遇到了 useEffect 和 useState 的問題,導致無限循環。
我創建了一個小點心來舉例說明正在發生的事情:
import React, { useEffect, useState, useMemo } from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
export default function App() {
const [testStateVariable, setTestStateVariable] = useState(false);
const useCustomHookTest = () => {
let testVar = ""; // Change the value "" to [] to see the problem;
return useMemo(
() => ({
testVar,
}),
[testVar],
);
};
var { testVar } = useCustomHookTest();
useEffect(() => {
console.log("CHANGE OF testVar!")
}, [testVar]);
return (
<View style={styles.container}>
<Button title="Change a state to true" onPress={() => {setTestStateVariable(true)}}/>
<Button title="Change a state to false" onPress={() => {setTestStateVariable(false)}}/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
});
基本上,當我在自定義掛鈎中使用數組時,它會在每次 state 發生變化時重新呈現並更改變量的值。 您可以看到將 testVar 的值更改為 [] 並按下兩個按鈕來更改狀態,每次 state 更改時都會觸發 useEffect,但是當值為“”時不會發生這種情況。
我嘗試使用 useMemo 但它沒有用,
有人有解決這個問題的方法嗎?
謝謝
基本要點是"" === ""
始終為真,而[] === []
始終為假。 字符串雖然類似於對象,但在某種程度上被視為原始類型,因為它們始終等於自身。 另一方面,Arrays 是特殊的 Javascript 對象,兩個聲明的對象永遠不會嚴格相等。
試試吧!
console.log('"" === ""', "" === ""); // true console.log('[] === []', [] === []); // false
當你分配let testVar = "";
每個渲染周期testVar
始終等於上一個渲染的最后一個值,因此useMemo
掛鈎返回先前計算的記憶值。
const useCustomHookTest = () => {
let testVar = "";
return useMemo(
() => ({
testVar
}),
[testVar] // "" === "" true, dependency didn't "change".
);
};
現在對比一下當你賦值let testVar = "";
每個渲染周期,依賴項現在總是一個新的數組引用,因此不嚴格相等,並且useMemo
掛鈎重新計算其記憶值。
const useCustomHookTest = () => {
let testVar = []; // <-- new array each render
return useMemo(
() => ({
testVar
}),
[testVar] // [] === [] false, new memoized value
);
};
由於記憶值是一個新的 object, useEffect
掛鈎的依賴項現在看到一個新的 object,即{ testVar: true } === { testVar: false }
不是假的,因為testVar
屬性改變了,而是因為{} === {}
也是錯誤的,原因與[] === []
相同。 這是一個新的 object 參考。
除此之外,您還不清楚您希望使用以下代碼模式完成什么:
export default function App() {
const [testStateVariable, setTestStateVariable] = useState(false);
const useCustomHookTest = () => {
let testVar = "";
return useMemo(
() => ({
testVar
}),
[testVar]
);
};
var { testVar } = useCustomHookTest();
useEffect(() => {
console.log("CHANGE OF testVar!");
}, [testVar]);
return (
<View style={styles.container}>
<Text>{testStateVariable.toString()}</Text>
<Button
title="Change a state to true"
onPress={() => {
setTestStateVariable(true);
}}
/>
<Button
title="Change a state to false"
onPress={() => {
setTestStateVariable(false);
}}
/>
</View>
);
}
useCustomHookTest
掛鈎在每個渲染周期都被重新聲明,因此在父組件的每個渲染周期都會重新創建內部的任何內容。
如果您的問題基本上是如何使用 arrays 作為 React 鈎子依賴項,那么不幸的是沒有任何好的解決方案。 兩個領先的解決方案要么實施深度相等性檢查,要么簡單地對數組進行 JSON 字符串化。
JSON.stringify 示例:
const useCustomHookTest = () => {
let testVar = [];
return { testVar };
};
export default function App() {
const [testStateVariable, setTestStateVariable] = useState(false);
const { testVar } = useCustomHookTest();
useEffect(() => {
console.log("CHANGE OF testVar!");
}, [JSON.stringify(testVar)]); // <-- JSON stringify dependency
return (
<View style={styles.container}>
<Text>{testStateVariable.toString()}</Text>
<Button
title={`Change a state to ${(!testStateVariable).toString()}`}
onPress={() => {
setTestStateVariable((t) => !t);
}}
/>
</View>
);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.