关于 useEffect 中的无限循环

boa*_*aol 5 reactjs react-hooks

我正在尝试使用 react hooks 构建一个 redux 进程,下面的代码是我想模拟一个ComponentDidMount带有 getUsers(redux action) 调用的函数,它是一个用于获取数据的 http 请求。

第一个版本是这样的

const { state, actions } = useContext(StoreContext);
const { getUsers } = actions;

useEffect(() => {
  getUsers();  // React Hook useEffect has a missing dependency: 'getUsers'.
}, []);
Run Code Online (Sandbox Code Playgroud)

但我收到了一个 linting 警告“React Hook useEffect 缺少依赖项:'getUsers'。在 useEffect 中包含它或删除依赖项数组”,

然后我将 getUsers 添加到依赖项数组,但在那里得到了无限循环

useEffect(() => {
  getUsers(); 
}, [getUsers])
Run Code Online (Sandbox Code Playgroud)

现在我通过使用 useRef 找到了解决方案

const fetchData = useRef(getUsers);

useEffect(() => {
  fetchData.current();
}, []);
Run Code Online (Sandbox Code Playgroud)

不确定这是否是正确的方法,但它确实解决了掉毛和无限循环(暂时?)

我的问题是:在代码的第二个版本中,究竟是什么导致了无限循环?每次渲染后,依赖数组中的 getUsers 是否更改?

jpe*_*erl 10

你的函数有依赖项,React 认为不列出这些依赖项是不安全的。假设您的函数取决于名为users. 显式列出依赖项数组中的隐式依赖项是行不通的:

useEffect(() => {
  getUsers();
}, [users]); // won't work
Run Code Online (Sandbox Code Playgroud)

然而,React 表示,推荐的解决方法是将函数移动到函数内部useEffect()。这样,警告不会说它缺少getUsers依赖项,而是依赖项getUsers

function Example({ users }) {

  useEffect(() => {
    // we moved getUsers inside useEffect
    function getUsers() {
      console.log(users);
    }
    getUsers();
  }, []); // users dependency missing
}
Run Code Online (Sandbox Code Playgroud)

因此,您可以指定users依赖项:

useEffect(() => {
    function getUsers() {
      console.log(users);
    }
    getUsers();
  }, [users]); // OK
Run Code Online (Sandbox Code Playgroud)

但是,您是从 props 获取该功能,它没有在您的组件中定义。

那该怎么办呢?解决您的问题的方法是记住您的功能。

useCallback 将返回回调的记忆版本,该版本仅在依赖项之一发生更改时才会更改。这在将回调传递给依赖引用相等的优化子组件时很有用,以防止不必要的渲染(例如 shouldComponentUpdate)。

你不能在你的组件中记住它,因为会有同样的警告:

const memoizedGetUsers = useCallback(
  () => {
    getUsers();
  },
  [], // same warning, missing the getUsers dependency
);
Run Code Online (Sandbox Code Playgroud)

解决方案是在getUsers定义的地方记住它,然后您将能够列出依赖项:

// wrap getUsers inside useCallback
const getUsers = useCallback(
  () => {
    //getUsers' implementation using users
    console.log(users);
  },
  [users], // OK
);

Run Code Online (Sandbox Code Playgroud)

在您的组件中,您将能够执行以下操作:

const { getUsers } = actions; // the memoized version

useEffect(() => {
    getUsers();
  }, [getUsers]); // it is now safe to set getUsers as a dependency

Run Code Online (Sandbox Code Playgroud)

至于存在无限循环的原因以及 useRef 工作的原因。我猜你的函数会导致重新渲染,并且在每次迭代时,都会重新创建 getUsers 并以无限循环结束。useRef 返回一个对象{ current: ... },使用 useRef 和{ current: ... }自己创建这个对象的区别在于useRef返回相同的对象而不是创建另一个对象。所以你可能使用了相同的功能。


小智 1

您应该在 useEffect 中声明 getUsers 它是您调用 getUsers 的唯一地方

useEffect(() => {
 const { getUsers } = props;
  getUsers();
}, []);
Run Code Online (Sandbox Code Playgroud)