在自定义挂钩中创建数组是在每次状态更改时重新渲染值

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,
  },
});

基本上,当我在自定义挂钩中使用数组时,它会在每次任何状态更改时重新呈现并更改变量的值。 您可以看到将 testVar 的值更改为 [] 并按下两个按钮来更改状态,在每次状态更改时都会触发 useEffect,但是当值为 "".

时不会发生这种情况

我试过使用 useMemo,但没用,

有人能解决这个问题吗?

谢谢

基本要点是 "" === "" 始终为真,而 [] === [] 始终为假。字符串,虽然像对象,但有点像基元,因为它们 总是 等于它们自己。另一方面,数组是特殊的 Javascript 对象,两个声明的对象永远不会严格相等。

试试吧!

console.log('"" === ""', "" === ""); // true
console.log('[] === []', [] === []); // false

当您分配 let testVar = ""; 每个渲染周期时,testVar 始终等于上一个渲染的最后一个值,因此 useMemo 挂钩 returns先前计算的记忆值。

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
  );
};

因为记忆值是一个 new 对象,useEffect 钩子的依赖现在看到一个新对象,即 { testVar: true } === { testVar: false } 不是假的,因为 testVar 属性 改变了,但是因为 {} === {} 也是假的,原因与 [] === [] 是一样的。这是一个新的对象引用。

除此之外,不清楚您希望使用以下代码模式完成什么:

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 挂钩,因此在父组件的每个渲染周期都会重新创建任何内部内容。

如果您的问题基本上是 如何 将数组用作 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>
  );
}