与 useEffect 一起使用时如何防止 useCallback 触发(并遵守 eslint-plugin-react-hooks)?

bin*_*tch 9 javascript typescript reactjs react-hooks eslint-plugin-react-hooks

我有一个用例,页面必须在第一次渲染和单击按钮时调用相同的获取函数。

代码类似于以下内容(参考:https ://stackoverflow -question-bink-62951987?file=index.tsx):

import React, { FunctionComponent, useCallback, useEffect, useState } from 'react';
import { fetchBackend } from './fetchBackend';

const App: FunctionComponent = () => {
  const [selected, setSelected] = useState<string>('a');
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<boolean>(false);
  const [data, setData] = useState<string | undefined>(undefined);

  const query = useCallback(async () => {
    setLoading(true)

    try {
      const res = await fetchBackend(selected);
      setData(res);
      setError(false);
    } catch (e) {
      setError(true);
    } finally {
      setLoading(false);
    }
  }, [])

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

  return (
    <div>
      <select onChange={e => setSelected(e.target.value)} value={selected}>
        <option value="a">a</option>
        <option value="b">b</option>
      </select>
      <div>
        <button onClick={query}>Query</button>
      </div>
      <br />
      {loading ? <div>Loading</div> : <div>{data}</div>}
      {error && <div>Error</div>}
    </div>
  )
}

export default App;
Run Code Online (Sandbox Code Playgroud)

对我来说,问题是 fetch 函数总是在任何输入更改时触发,因为eslint-plugin-react-hooks迫使我在钩子中声明所有依赖项(例如:选定状态)useCallback。我必须使用useCallback才能将它与useEffect.

我知道我可以将该函数放在组件之外并传递所有参数(props、setLoading、setError 等)以便使其正常工作,但我想知道是否可以在保留相同效果的同时存档相同的效果组件内部的 fetch 函数并遵守eslint-plugin-react-hooks


[更新] 对于任何有兴趣查看工作示例的人。这是从接受的答案中得出的更新代码。 https://stackblitz.com/edit/stackoverflow-question-bink-62951987-vxqtwm?file=index.tsx

Ros*_*mov 4

像往常一样添加所有依赖项useCallback,但不要在 useEffect 中创建另一个函数:

useEffect(query, [])

对于异步回调(例如您的情况下的查询),您需要将旧式的 Promise 方式与.then,.catch.finally回调一起使用,以便将 void 函数传递给useCallback,这是useEffect.

另一种方法可以在React 的文档中找到,但根据文档不推荐。

毕竟,传递给的内联函数useEffect无论如何都会在每次重新渲染时重新声明。使用第一种方法,只有当查询的依赖项发生变化时,您才会传递新函数。警告也应该消失。;)