如何正确使用useEffect挂钩和数组依赖项。我从redux商店传递了状态,但我的组件仍然无限渲染

shu*_*ary 1 javascript reactjs react-native redux react-hooks

我正在使用useEffect钩子,并使用函数getStoreUsers通过fetch调用获取用户数据列表,该函数对响应调度一个操作,并将shopUsers(这是一个数组)存储在redux存储中。

在数组依赖中,我正在写[shopUsers]。我不知道为什么它会导致无限渲染。

这是我使用useEffect挂钩的方式:

useEffect(() => {
    const { getStoreUsers, shopUsers } = props;
    setLoading(true);
    getStoreUsers().then(() => {
      setLoading(false);
    }).catch(() => {
      setLoading(false);
    });
  }, [shopUsers]);
Run Code Online (Sandbox Code Playgroud)

我只想在shopUsers数组中的数据更改时才重新渲染组件。

如果我在数组依赖项中编写shopUsers.length。它停止重新渲染。

但是,假设我有一个页面,当用户单击userList并在下一页更新用户数据时,该页面打开。更新之后,我希望用户返回到以前未卸载的相同组件。因此,在这种情况下,数组长度保持不变,但是数组索引中的数据将更新。因此,在这种情况下,shopUsers.length不起作用。

Col*_*rdo 5

您可以创建一个自定义钩子来执行您想要的操作:

在此示例中,我们替换了数组中的最后一个元素,并在控制台中看到了输出。

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { isEqual } from "lodash";

const usePrevious = value => {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};

const App = () => {
  const [arr, setArr] = useState([2, 4, 5]);
  const prevArr = usePrevious(arr);

  useEffect(() => {
    if (!isEqual(arr, prevArr)) {
      console.log(`array changed from ${prevArr} to ${arr}`);
    } 
  }, [prevArr]);

  const change = () => {
    const temp = [...arr];
    temp.pop();
    temp.push(6);
    setArr(temp);
  };

  return (
      <button onClick={change}>change last array element</button>
  )
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Run Code Online (Sandbox Code Playgroud)

这里的例子。


Kha*_*man 1

您的效果是基于“shopUsers”道具触发的,该道具本身会触发更新“shopUsers”道具的 redux 操作,这就是它不断无限触发的原因。

我认为你想要优化的是组件本身的渲染,因为你已经在使用 redux,我假设你的 props/state 是不可变的,所以你可以仅React.memo在其 props 之一时重新渲染你的组件改变。

此外,您还应该在钩子之外定义 state/props 变量,因为它们在整个函数的范围内使用,就像这样。

在你的情况下,如果你将一个空数组作为第二个参数传递给备忘录,那么它只会在 ComponentDidMount 上触发,如果你传递 null/未定义或不传递任何内容,它将在 ComponentDidMount + ComponentDidUpdate 上触发,如果你想优化即使 props 更改/组件更新,钩子也不会触发,除非特定变量发生更改,然后您可以添加一些变量作为第二个参数

React.memo(function(props){
  const [isLoading, setLoading] = useState(false);
  const { getStoreUsers, shopUsers } = props;
  useEffect(() => {
    setLoading(true);
    getStoreUsers().then(() => {
      setLoading(false);
    }).catch((err) => {
      setLoading(false);
    });
  }, []);
...
})
Run Code Online (Sandbox Code Playgroud)

  • 我不明白你想要做什么,这对我来说很奇怪......为什么你想更新一个变量,它本身用于触发更新自身的调用?我不明白这个用例,但最常见的是你想要做的是有一个父组件,它可以对 componentDidMount 进行 API 调用,或者使用空数组作为第二个参数的 useEffect(fn, []) 来触发一次,并且将结果作为 props 传递给子组件以渲染它们。然后,每当您想再次进行 API 调用时,它都应该位于父组件中的事件处理程序中,该事件处理程序将更新 props。 (2认同)
  • 您可以监听某个事件或传递一个回调 prop,当用户导航回来时触发 API 调用以再次获取数据,这比每次组件更新或 prop 更改时进行 API 调用要干净得多。 (2认同)