简体   繁体   English

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

[英]How to prevent useCallback from triggering when using with useEffect (and comply with eslint-plugin-react-hooks)?

I have a use-case where a page have to call the same fetch function on first render and on button click.我有一个用例,其中页面必须在第一次呈现和单击按钮时调用相同的提取 function。

The code is similar to the below (ref: https://stackblitz.com/edit/stackoverflow-question-bink-62951987?file=index.tsx ):代码类似于以下内容(参考: https://stackblitz.com/edit/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;

The problem for me is the fetch function always triggers on any input changed because eslint-plugin-react-hooks forces me to declare all dependencies (ex: selected state) in the useCallback hook.我的问题是 fetch function 总是在任何输入更改时触发,因为eslint-plugin-react-hooks强制我在useCallback挂钩中声明所有依赖项(例如:选定状态)。 And I have to use useCallback in order to use it with useEffect .而且我必须使用useCallback才能将其与useEffect一起使用。

I am aware that I can put the function outside of the component and passes all the arguments (props, setLoading, setError, ..etc.) in order for this to work but I wonder whether it is possible to archive the same effect while keeping the fetch function inside the component and comply to eslint-plugin-react-hooks ?我知道我可以将 function 放在组件外部并传递所有 arguments(props、setLoading、setError 等)以使其工作,但我想知道是否可以在保持相同效果的同时存档在组件内部获取 function 并遵守eslint-plugin-react-hooks


[UPDATED] For anyone who is interested in viewing the working example. [更新] 对于任何有兴趣查看工作示例的人。 Here is the updated code derived from the accepted answer.这是从已接受的答案派生的更新代码。 https://stackblitz.com/edit/stackoverflow-question-bink-62951987-vxqtwm?file=index.tsx https://stackblitz.com/edit/stackoverflow-question-bink-62951987-vxqtwm?file=index.tsx

Add all of your dependecies to useCallback as usual, but don't make another function in useEffect:像往常一样将所有依赖项添加到useCallback ,但不要在 useEffect 中创建另一个 function:

useEffect(query, [])

For async callbacks (like query in your case), you'll need to use the old-styled promise way with .then , .catch and .finally callbacks in order to have a void function passed to useCallback , which is required by useEffect .对于异步回调(如您的查询),您需要将旧式 promise 方式与.then.catch.finally回调一起使用,以便将 void function 传递给useCallback ,这是useEffect所要求的。

Another approach can be found on React's docs , but it's not recommended according to the docs.另一种方法可以在React 的文档中找到,但根据文档不推荐使用。

After all, inline functions passed to useEffect are re-declared on each re-render anyways.毕竟,传递给useEffect的内联函数无论如何都会在每次重新渲染时重新声明。 With the first approach, you'll be passing new function only when the deps of query change.使用第一种方法,只有当查询的部门发生变化时,您才会传递新的 function。 The warnings should go away, too.警告也应该 go 消失。 ;) ;)

There are a few models to achieve something where you need to call a fetch function when a component mounts and on a click on a button/other.有一些模型可以实现某些功能,您需要在安装组件和单击按钮/其他时调用fetch function Here I bring to you another model where you achieve both by using hooks only and without calling the fetch function directly based on a button click.在这里,我为您带来另一个 model ,您可以通过仅使用钩子而不是直接基于按钮单击调用fetch function来实现两者。 It'll also help you to satisfy eslint rules for hook deps array and be safe about infinite loop easily.它还将帮助您满足hook deps 数组的 eslint 规则,并轻松避免无限循环。 Actually, this will leverage the power of effect hook called useEffect and other being useState .实际上,这将利用名为useEffect和其他useState的效果钩子的力量。 But in case you have multiple functions to fetch different data, then you can consider many options, like useReducer approach.但是如果你有多个函数来获取不同的数据,那么你可以考虑很多选择,比如useReducer方法。 Well, look at this project where I tried to achieve something similar to what you wanted.好吧,看看这个项目,我试图在其中实现与您想要的类似的东西。

https://codesandbox.io/s/fetch-data-in-react-hooks-23q1k?file=/src/App.js https://codesandbox.io/s/fetch-data-in-react-hooks-23q1k?file=/src/App.js

Let's talk about the model a bit稍微说一下model

export default function App() {
  const [data, setDate] = React.useState("");
  const [id, setId] = React.useState(1);
  const [url, setUrl] = React.useState(
    `https://jsonplaceholder.typicode.com/todos/${id}`
  );
  const [isLoading, setIsLoading] = React.useState(false);

  React.useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(json => {
        setDate(json);
        setIsLoading(false);
      });
  }, [url]);

  return (
    <div className="App">
      <h1>Fetch data from API in React Hooks</h1>
      <input value={id} type="number" onChange={e => setId(e.target.value)} />
      <button
        onClick={() => {
          setIsLoading(true);
          setUrl(`https://jsonplaceholder.typicode.com/todos/${id}`);
        }}
      >
        GO & FETCH
      </button>
      {isLoading ? (
        <p>Loading</p>
      ) : (
        <pre>
          <code>{JSON.stringify(data, null, 2)}</code>
        </pre>
      )}
    </div>
  );
}

Here I fetched data in first rendering using the initial link, and on each button click instead of calling any method I updated a state that exists in the deps array of effect hook, useEffect , so that useEffect runs again.在这里,我使用初始链接在第一次渲染中获取数据,并且在每个按钮单击而不是调用任何方法时,我更新了效果挂钩 useEffect 的 deps 数组中存在的useEffect ,以便useEffect再次运行。

I think you can achieve the desired behavior easily as我认为您可以轻松实现所需的行为

useEffect(() => {
    query();
  }, [data]) // Only re-run the effect if data changes

For details, navigate to the end of this official docs page.有关详细信息,请导航至此官方文档页面的末尾。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Eslint React Hooks错误:eslint-plugin-react-hooks用尽详尽的警告警告useEffect中的功能依赖项 - Eslint React Hooks Error: eslint-plugin-react-hooks exhaustive deps warning for a function dependency in useEffect React Hooks useEffect 从 useCallback 调用道具回调 - React Hooks useEffect to call a prop callback from useCallback 如何设置useEffect来首先使用eslint-react-hooks从API获取数据? - how to set an useEffect to fetch data from API at first render with eslint-react-hooks? React Hooks useCallback 和 useEffect 导致反应应用程序中的无限循环 - React Hooks useCallback and useEffect causes infinite loop in react application 在 React 中使用 useEffect() 和 useCallback 获取数据 - Data Fetching Using useEffect() And useCallback In React 如何使用 useMemo 或 useCallback 在 React 中防止不必要的重新渲染? - How do i prevent unnecessary rerendering in React using useMemo or useCallback? 使用 React Hooks 时如何防止事件在嵌套的 div 中起作用 - How to prevent events from acting in nested divs when using React Hooks React挂钩:使用useEffect时无法获取状态更新 - React hooks: Not getting the state update when using useEffect 使用 React 中的 useEffect 和 useState 钩子从获取请求中管理数组 - Manage array from get request using useEffect and useState hooks in React 如何在 useEffect/useCallback-hook 中正确处理来自 React Context 的数据 - How to correctly work with data from React Context in useEffect/useCallback-hook
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM