【问题标题】:useEffect on infinite loop using async fetch function使用异步获取函数对无限循环的 useEffect
【发布时间】:2021-04-23 04:43:15
【问题描述】:

我试图理解为什么下面的 useEffect 在无限循环中运行。我制作了fetchSchedule 辅助函数来调用getSchedule 服务(使用Axios 查询API 端点)。我没有在 useEffect 挂钩中定义此函数的原因是因为我想在调用 onStatus 函数时也调用它(这会在单独的端点上切换布尔 PUT 请求)。

eslinter 要求将 fetchSchedule 添加到依赖项数组中,这似乎触发了无限循环。

它应该工作的方式是在第一次渲染时从数据库中获取数据,然后仅在每次更新 value 属性或切换 onStatus 按钮时。

到目前为止,我的研究似乎表明这可能与 useEffect 处理异步函数和闭包的方式有关。我仍在尝试理解 Hooks,显然我的代码中有些东西没有...

import React, { useEffect, useCallback } from 'react';
import useStateRef from 'react-usestateref';
import { NavLink } from 'react-router-dom';
import { getSchedule, updateStatus } from '../../services/scheduleService';
import Status from './status';
// import Pagination from './pagination';

const List = ({ value }) => {
  // eslint-disable-next-line
  const [schedule, setSchedule, ref] = useStateRef([]);
  // const [schedule, setSchedule] = useState([]);

  const fetchSchedule = useCallback(async () => {
    const { data } = await getSchedule(value);
    setSchedule(data);
  }, [value, setSchedule]);

  const onStatus = (id) => {
    updateStatus(id);
    fetchSchedule();
    console.log('fetch', ref.current[0].completed);
  };

  useEffect(() => {
    fetchSchedule();
  }, [fetchSchedule]);
return (...)

【问题讨论】:

标签: reactjs react-hooks


【解决方案1】:

2021 年 3 月更新

在与 react-usestateref 的 repo 所有者合作后,该软件包现在可以按原计划运行,并且可以安全地用作 useState 的替代版本 1.0.5。当前的实现如下所示:

function useStateRef(defaultValue) {
  var [state, setState] = React.useState(defaultValue);
  var ref = React.useRef(state);

  var dispatch = React.useCallback(function(val) {
    ref.current = typeof val === "function" ?
    val(ref.current) : val;

    setState(ref.current);
  }, []);

  return [state, dispatch, ref];
};

如果没有这个 react-usestateref 导入,你会没事的。

钩子返回一个普通的匿名函数来设置状态,这意味着它将在每次渲染时重新创建 - 你不能有用地将它包含在任何依赖数组中,因为它也会在每次渲染时更新。但是,由于该函数是从未知的自定义钩子返回的(无论如何,ESLint 会正确识别它不是正确的 setter 函数),如果不这样做,您会收到警告。

它试图解决的“问题”也会在你的代码中引入不好的做法——这是一种避免正确处理依赖关系的好方法,这些依赖关系可以让你的代码更安全。

如果您回到标准状态挂钩,我相信这段代码可以正常工作。与其尝试在onStatus 中获取状态的引用,不如将其设为异步并从 fetchSchedule 返回数据并进行设置。

const [schedule, setSchedule] = useState([]);

const fetchSchedule = useCallback(async () => {
  const { data } = await getSchedule(value);
  setSchedule(data);
  return data;
}, [value]);

const onStatus = async (id) => {
  updateStatus(id);
  const data = await fetchSchedule();
};

useEffect(() => {
  fetchSchedule();
}, [fetchSchedule]);

另外,虽然我不建议使用它,但实际上我们可以编写一个安全版本的 useStateRef 挂钩:

function useStateRef(defaultValue) {
    var [state, setState] = React.useState(defaultValue);

    var ref = React.useRef(defaultValue);
    ref.current = state; 

    return [state, setState, ref];
}

在组件的整个生命周期内,状态设置器函数始终在引用上相同,因此可以将其包含在依赖数组中,而不会导致重新创建效果/回调。

【讨论】:

  • 我不知道为什么useEffect() 在你的useStateRef() 钩子中,同步地做var ref = useRef(); ref.current = state; 不是更正确吗?否则 ref 实际上会引用之前的状态,直到渲染阶段完成。
  • @PatrickRoberts 我最初拥有它,但我改变了它,因为我的印象是在渲染完成之前没有任何依赖于state 的东西可以读取它的新值。因此,将refstate 绑定在一起效果会更接近于状态的实际工作方式。如果这不是很好的推理,很高兴将其改回。编辑:虽然我猜这不是为了工作状态实际上是如何工作的......
  • 我确实把它放回去了,因为我确实看到它更符合钩子的原始用途。
猜你喜欢
  • 2020-05-11
  • 2021-05-02
  • 2020-04-21
  • 2021-11-22
  • 1970-01-01
  • 2020-08-20
  • 2021-05-07
  • 1970-01-01
  • 2021-03-14
相关资源
最近更新 更多