简体   繁体   English

仅当我添加第二个依赖项时,才自定义 React 钩子,无限循环。 错误或我无法理解的东西?

[英]Custom React hook, infinite loop only if I add the second dependency. Bug or something I can't understand?

I've made a really simple React hook.我做了一个非常简单的 React 钩子。 That's something seen on many guides and websites:这是在许多指南和网站上看到的:

import { useEffect, useState } from 'react';
import axios from 'axios';

export const useFetchRemote = (remote, options, initialDataState) => {
  const [data, setData] = useState(initialDataState);

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios.get(remote, options);
      setData(result.data);
    };

    fetchData();
  }, [remote]);

  return data;
};

Example usage:示例用法:

import { useFetchRemote } from '../utils';

export const UserList = () => {
  const users = useFetchRemote('/api/users', {}, []);

  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>}
    </ul>
  );
}

This is working .这是有效的 If I understand correctly:如果我理解正确:

  • With no dependencies like useEffect(() => { /*...*/ }) , setting the state into the function would trigger a re-render, calling useEffect again, in an infinite loop.没有像useEffect(() => { /*...*/ })这样的依赖项,将 state 设置为 function 将触发重新渲染,在无限循环中再次调用useEffect
  • With empty dependencies like useEffect(() => { /*...*/ }, []) , my function will be called only the "very first time" component is mounted.对于useEffect(() => { /*...*/ }, [])类的空依赖项,我的 function 将仅在安装“非常第一次”组件时被调用。

So, in my case, remote is a dependency.所以,就我而言, remote是一个依赖项。 My function should be called again if remote changes.如果远程更改,应该再次调用我的 function。 This is true also for options .对于options也是如此。 If I add also options , the infinite loop starts.如果我还添加options ,则无限循环开始。 I can't understand... why this is happening?我不明白……为什么会这样?

export const useFetchRemote = (remote, options, initialDataState) => {
  // ...

  useEffect(() => {
      // ...
  }, [remote, options]);

  // ...
};

The infinite loop is caused by the fact that your options parameter is an object literal, which creates a new reference on every render of UserList .无限循环是由于您的options参数是 object 文字这一事实引起的,它会在UserList的每个渲染上创建一个新引用。 Either create a constant reference by defining a constant outside the scope of UserList like this:通过在UserList的 scope 之外定义一个常量来创建常量引用,如下所示:

const options = {};
const initialDataState = [];

export const UserList = () => {
  // or for variable options instead...
  // const [options, setOptions] = useState({});
  const users = useFetchRemote('/api/users', options, initialDataState);

  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>}
    </ul>
  );
}

or if you intend the options parameter to be effectively constant for each usage of the userFetchRemote() hook, you can do the equivalent of initializing props into state and prevent the reference from updating on every render:或者,如果您希望options参数对于userFetchRemote()钩子的每次使用都有效地保持不变,您可以执行将道具初始化为 state 的等效操作,并防止在每次渲染时更新引用:

export const useFetchRemote = (remote, options, initialDataState) => {
  const [optionsState] = useState(options);
  const [data, setData] = useState(initialDataState);

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios.get(remote, optionsState);
      setData(result.data);
    };

    fetchData();
  }, [remote, optionsState]);
  // ---------^

  return data;
};

This second approach will prevent a new fetch from occuring though, if the options are dynamically changed on a particular call site of useFetchRemote() .但是,如果选项在useFetchRemote()的特定调用站点上动态更改,则第二种方法将防止发生新的提取。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM