【问题标题】:How to get rid of endless requests?如何摆脱无休止的请求?
【发布时间】:2020-03-27 07:47:05
【问题描述】:

请帮忙解决useCallback,如何调用它才能让useEffect不会向服务器发送无休止的请求?现在它发送无尽的请求,我知道这个需要useCallback,但我不明白如何使用它

// Get data
useEffect(() => {
    api.getTestLeagues()
    .then(data => setLeagues(data));

    api.getTestCountries()
    .then(data => setCountries(data));

    if(sidebars === null && leagues !== null && countries !== null) {
        onTest()
    }

}, [api, leagues, sidebars, countries, onTest]);

// 'onTest' 函数使 useEffect Hook(第 29 行)的依赖关系在每次渲染时都发生变化。将它移到 useEffect 回调中。或者,将“onTest”定义包装到它自己的 useCallback() Hook 中

// test
const onTest = () => {
    const updateObj = {
        leagues: {
            title: "Мои лиги", 
            items: leagues
        },
        countries: {
            title: "Страны",
            items: countries
        }
    };

    setSidebars(updateObj);
};

要求:

【问题讨论】:

标签: reactjs


【解决方案1】:

我假设您想加载一次数据,通常使用 componentDidMount 完成。

2 useEffect 只触发一次,并行运行

useEffect(() => {
    api.getTestLeagues()
    .then(data => setSidebarItemsLeagues(data)); // for items 1 (not null)
}, []);

useEffect(() => {
    api.getTestCountries()
    .then(data => setSidebarItemsCountries(data)); // for items 2 (not     null)
}, [])

useEffect 仅在数据更改时运行

useEffect(() => {
  if(sidebars === null && leagues !== null && countries !== null) {
    const updateObj = {
      leagues: {
          title: "Мои лиги", 
          items: leagues
      },
      countries: {
          title: "Страны",
          items: countries
      }
    };

    setSidebars(updateObj);
  }
}, [leagues, countries]);

更新

api 早先定义...

const api = new Services();

当然,每次渲染都会发生变化,但不应被视为依赖项,因为它总是使用相同的 url - 它们没有参数化。没有有意义(影响数据)的更改 - 不在乎。

如果api 没有传递给渲染的组件(ref cange 可能会强制重新渲染),那么不需要关心它

你可以摆脱这个定义

useEffect(() => {
    new Service().getTestLeagues()
    .then(data => setSidebarItemsLeagues(data)); // for items 1 (not null)
}, []);

useEffect(() => {
    new Service().getTestCountries()
    .then(data => setSidebarItemsCountries(data)); // for items 2 (not     null)
}, [])

即使它看起来是不必要的重复实例它可能更优化当组件频繁重新渲染时 - 就像在打开-关闭视图更改中一样。

【讨论】:

  • 您的答案不包括组件范围内随时间变化的所有值(apisidebarsonTest )以及效果使用的所有值。您的代码将引用以前渲染中的陈旧值。请参阅本节中的注释:reactjs.org/docs/…
  • @xadm 你说的有道理,但是添加依赖项是一件好事。 React 还提到了一些警告。我们知道api 只是一个类,它有两个不会动态变化的函数,但对于反应它只是另一个变量。
  • 需要时 - 像(每次渲染更改)事件处理程序 fn 引用导致重新渲染 - 不是这种情况
  • 我假设(根据昨天的问题)构建侧边栏菜单需要它 - 一旦触发,通常使用 componentDidMount
【解决方案2】:

您处于无限循环中,因为您的效果取决于您在其中更改的值:每次从 api 获取一些数据时,您都会更改 leaguescountries 状态,这会导致再次运行效果。

拆分效果,使请求效果不依赖于它改变的值:

useEffect(() => {
  api.getTestLeagues().then(data => setLeagues(data));
  api.getTestCountries().then(data => setCountries(data));
}, [api]);

useEffect(() => {
  if(sidebars === null && leagues !== null && countries !== null) {
    onTest();
  }
}, [leagues, sidebars, countries, onTest]);

还有:

  • 在您的组件之外定义api(上图),以便值保持稳定。
  • 您可以在效果之前将onTest 包裹在useCallback 中,就像@Ramesh 建议的那样。

【讨论】:

  • 很奇怪,api可能会改变,我会更新代码让你记住它
  • 组件中是否定义了api?不要使用useMemo,只需在组件上方定义即可。
【解决方案3】:

尝试使用 useCallback 包装 onTest,如下所示:

const onTest = useCallback(() => {
    const updateObj = {
        leagues: {
            title: "Мои лиги", 
            items: leagues
        },
        countries: {
            title: "Страны",
            items: countries
        }
    };

    setSidebars(updateObj);
},[]);

useCallback 的第二个参数是一个类似于useEffect 的依赖数组。

顺便说一句,这将记住函数,这样就不会在每次渲染时都创建一个新函数。

更新

最好将 useEffect 拆分,以便每个 useEffect 只专注于在其依赖关系发生更改时执行某些操作。

useEffect(() => {
    api.getTestLeagues()
    .then(data => setLeagues(data));

    api.getTestCountries()
    .then(data => setCountries(data));
}, [api]);

useEffect(() => {
    if(sidebars === null && leagues !== null && countries !== null) {
        onTest()
    } 
},[onTest, sidebars, leagues, countries])

当您发送一个更改状态的请求并且还使用与将再次发送请求的依赖项相同的状态(因为状态的值发生变化)时,它将导致无限循环。

更新 2

问题可能出在 api 类实例上,因为在每次新渲染时都会创建一个新实例。

你说你在做这样的事情const api = new Services(); 所以用 useMemo 像这样包装它:

const api = useMemo(() => {
 return new Services();
})

【讨论】:

  • api 是如何定义的?
  • ReferenceError: 初始化前无法访问'onTest' i.imgur.com/KrzNW22.png
  • @reacter 确保在调用它之前定义了onTest
  • @reacter 将onTest移动到useEffect上方
  • @reacter 更新了我的答案,最好拆分 useEffect 以便您可以摆脱警告。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-26
  • 1970-01-01
  • 1970-01-01
  • 2020-12-19
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多