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

基本上,当我在自定义钩子中使用数组时,它会在每次状态更改时重新呈现并更改变量的值.

我试着用useMemo,但没用,

有人能解决这个问题吗?

谢谢

推荐答案

基本要点是"" === ""总是正确的,而[] === []总是错误的.字符串虽然与对象类似,但在某种程度上被视为基本体,因为它们将与自身相等.另一方面,数组是特殊的Javascript对象,两个声明的对象永远不会严格相等.

试试看!

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

当您 for each 渲染周期指定let testVar = "";时,testVar始终等于上一次渲染的最后一个值,因此useMemo钩子将返回之前计算的、已记忆的值.

const useCustomHookTest = () => {
  let testVar = "";
  return useMemo(
    () => ({
      testVar
    }),
    [testVar] // "" === "" true, dependency didn't "change".
  );
};

与此形成对比的是,现在当您 for each 渲染周期指定let testVar = "";时,依赖项现在是always新的数组引用,因此不完全相等,useMemo挂钩将重新计算其记忆值.

const useCustomHookTest = () => {
  let testVar = []; // <-- new array each render
  return useMemo(
    () => ({
      testVar
    }),
    [testVar] // [] === [] false, new memoized value
  );
};

由于记忆值是105对象,因此useEffect钩子的依赖关系现在会看到一个新对象,即{ testVar: true } === { testVar: false }不是因为testVar属性发生了更改而为false,而是因为{} === {}也是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钩子,因此在父组件的每个渲染周期都会重新创建任何内部内容.

如果你的问题基本上是使用数组作为React-hook依赖项,那么不幸的是,没有任何好的解决方案.两种主要的解决方案要么实现深度平等性判断,要么简单地对数组进行JSON字符串化.

JSON.字符串示例:

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

Edit creating-an-array-in-the-custom-hook-is-re-rendering-value-at-every-state-change

Javascript相关问答推荐

Redux工具包查询(RTKQ)端点无效并重新验证多次触发

从PWA中的内部存储读取文件

Spring boot JSON解析错误:意外字符错误

如何通过将实例方法的名称传递给具有正确类型的参数的继承方法来调用实例方法?

如何在Svelte中从一个codec函数中调用error()?

一个实体一刀VS每个实体多刀S

当代码另有说明时,随机放置的圆圈有时会从画布上消失

是否可以将Select()和Sample()与Mongoose结合使用?

删除元素属性或样式属性的首选方法

在表单集中保存更改时删除';禁用';

如何在和IF语句中使用||和&;&;?

使用API调用的VUE 3键盘输入同步问题

TypeORM QueryBuilder限制联接到一条记录

固定动态、self 调整的优先级队列

从异步操作返回对象

递归地将JSON对象的内容上移一级

Reaction:从子组件调用父组件中的函数

如何解析/data/build中的Angularnode_modules/chart.js/dist/Chart.js?

有没有一种方法可以在不定义任何属性的情况下使用CSS转换?

TypeScrip Types-向流程对象添加属性