【问题标题】:Why doesn't my useEffect go into an infinite loop when modifying it's dependency?为什么我的 useEffect 在修改它的依赖项时没有进入无限循环?
【发布时间】:2021-12-30 19:39:58
【问题描述】:

代码

PrizeHistory.js

...

const PrizeHistory = () =>  {
  const [history, setHistory] = useState([]);

  useEffect(() => {

    async function getHistory () {
      try {
        let result = await sqliteInterface.getAllPrizes(db, prizeTable);
        result == null ? console.log("res is null") : setHistory(result);
        console.log("result" + result);
        console.log("history" + history);
      } catch (err) {
        console.log(err);
      }
    }

    getHistory();
  }, [history])

  return (
    <View style={styles.body}>
      <Text style={styles.text}>Prize History</Text>
    </View>
  );
}

getAllPrizes

getAllPrizes(db, tableName) {
    return new Promise((resolve, reject) => {
      db.transaction((tx) => {
        tx.executeSql(
          `SELECT * FROM ${tableName};`, 
          [],
          (tx, res) => {
            let len = res.rows.length;
            let results = [];  

            if(len > 0) {
              for(i = 0; i < res.rows.length; i++) {
                results.push(res.rows.item(i));
              }
            } 
            console.log("resolving promise now");
            resolve(JSON.stringify(results));
          },
          (error) => {
            reject(Error(`SQLite getAllPrizes: failed to get '*' from ${tableName}: ` + error));
          }
        );
      }); 
    });
  }

目标

当页面挂载时,将历史状态设置为数据库中的数据。我最初尝试过这个:

  useEffect(() => {

    async function getHistory () {
      try {
        let result = await sqliteInterface.getAllPrizes(db, prizeTable);
        result == null ? console.log("res is null") : setHistory(result);
        console.log("result" + result);
        console.log("history" + history);
      } catch (err) {
        console.log(err);
      }
    }

    getHistory();
  }, [])

但它从未正确设置我的历史变量。我刚得到一个空数组(这是我最初将历史状态设置为的)。我希望历史变量等于历史console.log() 中的结果变量,但事实并非如此。

所以我把useEffect()改成了这样:

  useEffect(() => {

    async function getHistory () {
      try {
        let result = await sqliteInterface.getAllPrizes(db, prizeTable);
        result == null ? console.log("res is null") : setHistory(result);
        console.log("result" + result);
        console.log("history" + history);
      } catch (err) {
        console.log(err);
      }
    }

    getHistory();
  }, [history])

这段代码正确地更改了历史变量,但我预计它会进入无限循环......但它没有?这是我的逻辑

  1. useEffect() 具有 [history] 作为依赖项
  2. onMount,history 设置为默认值[]
  3. useEffect() 看到 history 已设置并开始运行
  4. useEffect() 重置 history
  5. useEffect() 应该重新开始,因为它会重置 history 变量本身
  6. 无限重复 3-5...

但它不会无限重复......

我的问题

  1. [history] 设置为它的依赖项时,为什么 useEffect() 没有无限运行
  2. 为什么useEffect() 没有在挂载时正确设置我的历史变量,而它有[] 作为它的依赖项。

【问题讨论】:

  • 你为什么期望 useEffect() 无限运行。 “历史”是一个状态变量。难道不应该只在变量“history”更新时才执行吗?

标签: javascript reactjs react-hooks use-effect


【解决方案1】:

您的 1 个问题的答案是:history 在第一次设置后不会更改,因为您的 getAllPrizes 返回始终相同的值,因此 useEffect 仅触发一次(在安装时)。

对于第 2 个问题,问题是您试图在调用 setHistory 后立即记录 history 值,这是异步的。如果您想在每次更新时记录history,您必须执行以下操作:

useEffect(() => {
  console.log(history)
}, [history])

【讨论】:

  • 这对问题 1 有意义。对于问题 2,useEffect() 具有 console.log("result" + result);console.log("history" + history);。第二个日志打印为空。
  • 好的,现在很清楚了。 history 是空的,因为 setHistory 是异步的,所以在调用 setHistory 之后,history 不会立即更新。如果您想在每次更新时记录 history 的值,您必须将 console.log 放在带有 dep 数组 [history]useEffect 中。
  • 你是对的。非常感谢
  • @KraveXL 很高兴我能帮上忙。如果答案解决了您的问题,请考虑将其标记为已接受,以帮助其他人快速找到解决方案。谢谢。
【解决方案2】:

你确定你没有尝试在这一行设置字符串类型:

result == null ? console.log("res is null") : setHistory(result);

据我所知,初始类型是一个数组,resolve 函数返回一个字符串化对象。 您是否在控制台中收到任何警告?

【讨论】:

  • 是的,你是对的。我已经意识到这一点,并计划在正确设置变量后对其进行修复。我可以随时更改状态的变量类型。我最初将历史设置为一个数组作为测试。感谢您指出这一点
猜你喜欢
  • 1970-01-01
  • 2020-01-11
  • 2020-11-02
  • 2021-09-27
  • 2020-11-23
  • 2020-11-10
  • 2019-12-31
  • 1970-01-01
相关资源
最近更新 更多