【问题标题】:React setInterval via Hook - Invalid hook call通过 Hook 反应 setInterval - 无效的钩子调用
【发布时间】:2019-11-29 20:45:39
【问题描述】:

我想定期执行一个动作(总是在 4 秒后)。

我以这个为例:https://overreacted.io/making-setinterval-declarative-with-react-hooks/

问题:

未捕获的不变违规:无效的钩子调用。 Hooks 只能在函数组件的主体内部调用。这可能由于以下原因之一而发生: 1. React 和渲染器的版本可能不匹配(例如 React DOM) 2. 你可能违反了 Hooks 规则 3. 您可能在同一个应用中拥有多个 React 副本

我知道 React Hooks 只能在主上下文中使用,而不能在子上下文中使用。这正是我的问题。我想使用一个函数来创建我的区间。

也许有完全不同的方法。

这就是我目前所拥有的,有人可以帮忙吗?

import React, { useState, useEffect, useRef } from 'react';
import { List } from 'antd';
import axios from 'axios';
import isRequestPerformOk from '../../utils/isRequestPerformOk';
import assertChain from '../../utils/assertChain';
import getConfigs from '../../utils/getConfigs';

const useInterval = (callback, delay) => {
  const savedCallback = useRef();

  // Remember the latest function.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  // eslint-disable-next-line
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
};

const T3nNews = () => {
  const [data, setData] = useState({ entries: [] });
  const configs = getConfigs();

  const updateDataFn = async (uri) => {
    const res = await axios.get(uri);
    const entriesList = assertChain(res, 'data.entries')
      ? res.data.entries
      : [];
    setData({ entries: entriesList });
  };

  const setUpDataFn = (uri) => {
    useInterval(() => {
      if (isRequestPerformOk() || true) {
        updateDataFn(uri);
      }
    }, 4000);
    updateDataFn(uri);
  };

  setUpDataFn(configs.uris.t3nRSS);

  const createMarkup = description => ({ __html: description });
  return (
    <List
      className="t3nNews"
      itemLayout="horizontal"
      dataSource={data.entries}
      renderItem={item => (
        <List.Item>
          <List.Item.Meta
            className="meta"
            title={item.title}
            description={
              <p dangerouslySetInnerHTML={createMarkup(item.description)} />
            }
          />
        </List.Item>
      )}
    />
  );
};
export default T3nNews;

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    你在调用useInterval(...)时调用useEffectinside anotheruseEffect,这是被禁止的。

    很可能,这正是您想要的:

    const T3nNews = () => {
      const [data, setData] = useState({ entries: [] });
      const configs = getConfigs();
    
      const updateDataFn = async (uri) => {
        const res = await axios.get(uri);
        const entriesList = assertChain(res, 'data.entries')
          ? res.data.entries
          : [];
        setData({ entries: entriesList });
      };
    
      useInterval(() => {
        if (isRequestPerformOk() || true) {
          updateDataFn(configs.uris.t3nRSS);
        }
      }, 4000);
    
      const createMarkup = description => ({ __html: description });
      return (
        <List
          className="t3nNews"
          itemLayout="horizontal"
          dataSource={data.entries}
          renderItem={item => (
            <List.Item>
              <List.Item.Meta
                className="meta"
                title={item.title}
                description={
                  <p dangerouslySetInnerHTML={createMarkup(item.description)} />
                }
              />
            </List.Item>
          )}
        />
      );
    };
    

    注意:useInterval 应该在组件之外定义,以避免每次渲染都重新创建钩子(但这不是问题)。

    【讨论】:

    • 嗨@Matthieu Libeer,我现在这样做了,但给了我同样的错误。我在最初的帖子中编辑了代码。可以再看看吗?
    • 你有没有注意到我删除了对useEffect的调用,现在只调用useInterval
    • 无论哪种方式我都需要那个 useEffect,因为我还需要稍后处理 componentWillUnmount() 等价物..
    • 你可以在useInterval旁边有另一个useEffect,但是你不能在useEffect里面有useInterval。这是您的问题的根源。
    • 好吧,但是如何解决我的问题呢?如果组件已安装,我想启动该 setInterval,因此我需要将其置于 useEffect 中。不知道如何用反应钩子解决它。如何在不将 useInterval 放入 useEffect 的情况下运行它?
    【解决方案2】:

    您的挂钩定义看起来不错,但您无法在组件内定义自定义挂钩。如果你看文章中的CodeSandbox,你可以看到钩子是在外面定义的。

    只需将useInterval 定义移到T3nNews 组件之外,它就可以工作(并且您可以在其他组件中使用它!)。

    【讨论】:

    • 嗨 vinsanity555,我现在这样做了,但给了我同样的错误。我在最初的帖子中编辑了代码。可以再看看吗?
    猜你喜欢
    • 2020-12-02
    • 1970-01-01
    • 2021-08-17
    • 1970-01-01
    • 2019-08-17
    • 2019-05-27
    • 2019-09-25
    • 1970-01-01
    • 2021-01-16
    相关资源
    最近更新 更多