我在 React Native 中创建自定义钩子(Hook),遇到了 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,
},
});
基本上,当我在自定义 Hook 中使用数组时,每次任何状态更改时,它都会重新渲染并更改变量的值。 您可以看到这将 testVar 的值更改为 [] 并按下两个按钮来更改状态,每次状态更改时都会触发 useEffect,但当值为“”时不会发生这种情况。
我尝试使用 useMemo 但它不起作用,
有人能解决这个问题吗?
谢谢
最佳答案
基本要点是 ""=== ""
始终为 true,而 [] === []
始终为 false。字符串虽然像对象,但在某种程度上被视为基元,因为它们总是等于它们自己。另一方面,数组是特殊的 Javascript 对象,两个声明的对象永远不会严格相等。
试试吧!
console.log('"" === ""', "" === ""); // true
console.log('[] === []', [] === []); // false
当您在每个渲染周期分配 let testVar = "";
时,testVar
始终等于上一次渲染的最后一个值,因此 useMemo
钩子(Hook)返回之前计算的、内存的值。
const useCustomHookTest = () => {
let testVar = "";
return useMemo(
() => ({
testVar
}),
[testVar] // "" === "" true, dependency didn't "change".
);
};
现在对比一下,当您在每个渲染周期分配 let testVar = "";
时,依赖项现在始终是一个新的数组引用,因此不严格相等,并且 useMemo
钩子(Hook)重新计算其内存值。
const useCustomHookTest = () => {
let testVar = []; // <-- new array each render
return useMemo(
() => ({
testVar
}),
[testVar] // [] === [] false, new memoized value
);
};
由于内存值是一个新对象,useEffect
钩子(Hook)的依赖现在看到一个新对象,即{ testVar: true } === { testVar: false }
不是 false,因为 testVar
属性已更改,而是因为 {} === {}
是出于同样的原因 [] === []
也是 false。这是一个新的对象引用。
除此之外,还不清楚您希望使用以下代码模式完成什么:
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
钩子(Hook)在每个渲染周期都会重新声明,因此父组件的每个渲染周期都会重新创建任何内部内容。
如果您的问题基本上是如何使用数组作为 React hook 依赖项,那么不幸的是没有任何好的解决方案。两个领先的解决方案要么实现深度相等检查,要么简单地对数组进行 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>
);
}
关于javascript - 在自定义 Hook 中创建数组会在每次状态更改时重新渲染值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72093872/