[英]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.