Reactjs 使用异步获取函数对无限循环的使用效果

Reactjs 使用异步获取函数对无限循环的使用效果,reactjs,react-hooks,Reactjs,React Hooks,我试图理解为什么下面的useffect是在一个无限循环中运行的。我使用fetchSchedulehelper函数调用getSchedule服务(使用Axios查询API端点)。我没有在useEffect钩子中定义此函数的原因是,我希望在调用onStatus函数时也调用它(在单独的端点上切换布尔PUT请求) eslinter要求将fetchSchedule添加到依赖项数组中,这似乎触发了无限循环 它的工作方式是在第一次渲染时从数据库获取数据,然后仅在每次更新值道具或切换onStatus按钮时执行

我试图理解为什么下面的useffect是在一个无限循环中运行的。我使用
fetchSchedule
helper函数调用
getSchedule
服务(使用Axios查询API端点)。我没有在useEffect钩子中定义此函数的原因是,我希望在调用
onStatus
函数时也调用它(在单独的端点上切换布尔PUT请求)

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

它的工作方式是在第一次渲染时从数据库获取数据,然后仅在每次更新
道具或切换
onStatus
按钮时执行

到目前为止,我的研究似乎指出,这可能与useEffect在异步函数和闭包中的行为方式有关。我仍在努力理解钩子,很明显,我的代码中有一些东西我没有理解

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 (...)

2021年3月更新

在与回购所有人就
react usestateref
进行合作后,该软件包现在可以按照最初的预期运行,并且可以安全地作为
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
import,您会很好

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

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

如果你回到一个标准的状态钩子,我相信这段代码会很好的工作。不要试图在状态上获取状态的引用,也要使其异步,并从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];
}

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

这个答案可能对您有所帮助?我不确定为什么
useffect()
在你的
useStateRef()
hook中,只做
var ref=useRef()不是更正确吗;参考电流=状态同步?否则,在渲染阶段完成之前,ref实际上会引用上一个状态。@PatrickRoberts我原来是这样做的,但我改变了它,因为我觉得在渲染完成之前,依赖于
状态的任何东西都无法读取它的新值。因此,将
ref
state
绑定在一起,并产生一种效果,将更接近于state的实际工作方式。如果这不是一个很好的理由,我很乐意把它改回去。编辑:虽然我想这并不是为了研究状态实际上是如何工作的……但我确实把它放回去了,因为我确实看到了它是如何更符合钩子的最初目的的。