Reactjs自定义钩子调用无限循环

时间:2019-08-27 12:13:26

标签: infinite-loop components fetch

我正在创建我的第一个自制的Web开发项目,并且遇到了无限循环。完整项目可在https://github.com/Olks95/my-dnd/tree/spellbook上找到。所以问题是:是什么原因导致了循环,我该如何解决?

(调用ContentSelector-Spellbook时,循环会在“游乐场”组件的第二项中的某处发生。自定义钩子useHandbook在Spellbook中被调用并连续调用API,显然应该只发生一次...刷新或单击返回以停止垃圾邮件)

据我所知,这个问题不在自定义钩子本身中,因为我已经进行了多次尝试来重写它,并且在useEffect()的末尾添加了一个空的依赖项数组。我将在此处尝试使用示例代码进行解释。

import { Component1, Component2, Component3 } from './ContentSelector.js';

const components = {
  option1: Component1,
  option2: Component2
  option3: Component3
}

const Playground = (props) => {
  const LeftItem = components['option1']
  const MiddleItem = components['option2']
  const RightItem = components['option3']
  ...
}

我希望能够选择在每个元素中放置什么内容,并最终制作一个ContentSelector组件,该组件将所有内容组件都放在一个文件中,并分别导入/导出。这似乎是一种奇怪的方法,但这是我发现使其起作用的唯一方法。 (是否可能是导致循环的原因?)由于在开发中还处于初期阶段,因此对选择进行了硬编码。 item变量以大写字母开头,因此以后我可以将它们作为组件渲染,如下所示:

<LeftItem ...some properties... />

游乐场然后返回以下内容进行渲染:

return(
 <React.Fragment>
  <div className="container">
    <div className="flex-item">
      /* Working select-option to pass correct props to Component1 */
      <div className="content">
        <LeftItem ...some properties... />
      </div>
    </div
    <div className="flex-item">
      /* Currently the same selector that changes the content of the LeftItem */
      <div className="content">
        <MiddleItem ...some properties... />
      </div>
    </div>
    /*RightItem follows the same formula but currently only renders "coming soon..." */
  </div>
 </React.Fragment>
)

然后,内容选择器具有三个组成部分:

Component1:调用仅运行一次的自定义钩子。然后将信息发送到另一个组件进行渲染。一切正常。

Component2:无限次调用自定义钩子,但预期其工作方式与组件1相同...

Component3:渲染即将推出...

请参阅下面的组件1和2:

export const Component1 = (props) => {
    const [ isLoading, fetchedData ] = useDicecloud(props.selectedChar);
    let loadedCharacter = null;

    if(fetchedData) {
        loadedCharacter = {
          name: fetchedData[0].Name,
          alignment: fetchedData[0].Alignment,
          /* a few more assignments */
        };
      }

    let content = <p>Loading characters...</p>;

    if(!isLoading && fetchedData && fetchedData.length > 0) {
        content = (
            <React.Fragment>
                <Character 
                    name={loadedCharacter.name}
                    alignment={loadedCharacter.alignment}
                    /* a few more props */ />
            </React.Fragment>
        )
    }
    return content;
}
export const Component2 = (props) => {
    const [ fetchedData, error, isLoading ] = useHandbook('https://cors-anywhere.herokuapp.com/http://dnd5eapi.co/api/spells/?name=Aid')

    let content = <p>Loading spells...</p>;

    if(!isLoading && fetchedData) {
/* useHandbook used to return text to user in a string in some situations */
        if(typeof fetchedData === 'string') {
            content = (
                <React.Fragment>
                    <p> {fetchedData} </p>
                </React.Fragment>
            )
        } else {
            content = (
                <React.Fragment>
                    <Spellbook
/* the component that will in the future render the data from the API called in useHandbook */
                     />
                </React.Fragment>
            )
        }
    }
    return content;
}

我已经在这个问题上工作了几天,随着我的前进,它变得越来越混乱。我希望该错误出现在useHandbook中,但经过多次重制后,似乎并非如此。当前的useHandbook非常简单,如下所示。

export const useHandbook = (url) => {
    const [ isLoading, setIsLoading ] = useState(false);
    const [ error, setError ] = useState(null);
    const [ data, setData ] = useState(null);

        const fetchData = async () => {
            setIsLoading(true);
            try {
                const res = await fetch(url, {
                    method: "GET",
                    mode: 'cors'
                });
                const json = await res.json();
                setData(json);
                setIsLoading(false);
            } catch(error) {
                setError(error);
            }
        };

    useEffect(() => {
        fetchData();
    }, []); //From what the documentation says, this [] should stop it from running more than once.
    return [ data, error, isLoading ];
};

编辑:我运行了带有react扩展的chrome开发人员工具,并看到了一些有用的东西:

Image showing Component2 (Spellbook) run inside itself infinite times

2 个答案:

答案 0 :(得分:0)

您可以修改自定义挂钩useHandbook's useEffect挂钩,以将URL作为依赖项,因为useEffectcomponentWillMountcomponentDidUpdate和{{1 }},您的情况是多次componentWillUnmount。所以你可以做的。

componentDidUpdate

因为除非URL发生更改,否则无需重新获取数据

答案 1 :(得分:0)

我发现了错误。 Component2的真实名称是Spellbook,我也称其为尚未制作的渲染组件。事实证明,我是从内部调用该组件的。

易于在问题的编辑部分的图像中看到。

相关问题