useEffect重新渲染次数过多

9
我有这个组件,需要获取数据,将其设置为状态,然后传递给子组件。其中一些数据还需要设置在上下文中。我的问题是,使用useEffect一旦调用API,它就会为我需要执行的每个setvalue()函数重新渲染。我已经尝试向useEffect传递一个空[]数组,但由于状态正在改变,仍然会得到相同数量的重新渲染。目前,该数组包含set...函数以防止eslint引发警告。
有没有更好的方法来避免如此多的重新渲染?
const Home = (props) => {

  console.log("TCL: Home -> props", props);
  const classes = useStyles();
  const [value, setValue] = React.useState(0);


  //CONTEXT
  const { listSavedJobs, setListSavedJobs, setIsFullView} = useContext(HomeContext);
  const {
    setUserName,
    setUserLastName,
    setUserEmail,
    setAvatarProfile,
  } = useContext(UserContext);

  // STATE
  const [searchSettings, setSearchSettings] = useState([]);
  const [oppData, setOppData] = useState([]);
  const handleChange = (event, newValue) => {
    setValue(newValue);
  };


  const handleChangeIndex = index => {
    setValue(index);
  };


  //API CALLS
  useEffect(() => {
    const triggerAPI = async () => {

      setIsFullView(false);

      const oppResponse = await API.getOpportunity();
      if(oppResponse){
        setOppData(oppResponse.response);
      }
      const profileResponse = await API.getUserProfile();
      if(profileResponse){
        setUserName(profileResponse.response.first_name);
        setUserLastName(profileResponse.response.last_name);
        setUserEmail(profileResponse.response.emailId);
      }
      const profileExtData = await API.getUserProfileExt();
      if(profileExtData){
        setAvatarProfile(profileExtData.response.avatar);
        setListSavedJobs(profileExtData.response.savedJobs);
        setSearchSettings(profileExtData.response.preferredIndustry);
      }
    };
    triggerAPI();

  }, [ 
    setOppData,
    setUserName,
    setUserLastName,
    setUserEmail,
    setAvatarProfile,
    setListSavedJobs,
    setIsFullView,
  ]);

...```





1
如果状态更改是异步触发的(例如包装在Promise中),它们将不会被批处理。您可能在async函数中过多地使用了set,每个set都会导致重新渲染,这太多了。我不知道你说的“太多”是指“太多”还是“无限”? - Loi Nguyen Huynh
如果API.getOpportunity()API.getUserProfile()API.getUserProfileExt()3个独立的API调用,这意味着结果将异步返回,那么为了获取3个API调用的结果,重新渲染3次(每次重新渲染都是为了获取一个结果)是可以接受的(但在你的情况下可能是8次)?你所说的太多的重新渲染是多少? - Loi Nguyen Huynh
有一些问题:如果setUserNamesetUserLastNamesetUserEmail总是一起设置,那么你应该使用只有一个状态对象叫做user **而不是3个状态变量userNameuserLastNameuserEmail**,这样可以在这种情况下节省2次重新渲染。另一个问题是你使用await的方式会导致3个API调用同步运行而不是并行运行,这不会导致更多的重新渲染,但会花费更多的性能,要解决这个问题,你需要使用Promise.all - Loi Nguyen Huynh
1
@LoiNguyenHuynh 谢谢您,这些是非常宝贵的建议,我会实施的。 - Andrea
3个回答

8

useEffect的第二个参数中传递一个空数组即可。

注意

React保证setState函数的标识在重新渲染时是稳定的,不会改变。因此,从useEffect或useCallback依赖列表中省略它是安全的。 来源

编辑:尝试这个来避免重新渲染。请谨慎使用。


看起来依赖列表中的set...是多余的,但不会导致重新渲染? - Loi Nguyen Huynh
我已经尝试清空数组,但仍然收到相同数量的重新渲染。我已将该集合列入依赖项以清除来自react-hooks/exhaustive-deps的ESLINT警告。 - Andrea
@Loi 您是正确的,我误解了问题。 - Alexander Vidaurre Arroyo
@LoiNguyenHuynh,我总共获得了10次重新渲染。 - Andrea

4

只在挂载和卸载时运行

您可以传递空数组[]作为空指令,表示“只在挂载和卸载时运行”。因此,如果我们将上面的组件更改为像这样调用useEffect

useEffect(() => {
  console.log('mounted');
  return () => console.log('unmounting...');
}, [])

然后,在初始渲染之后,它将打印“mounted”,在其生命周期内保持静默,并在退出时打印“unmounting…”。

防止useEffect在每次渲染时运行

如果您希望效果不那么频繁地运行,可以提供第二个参数——一个值数组。把它们看作该效果的依赖项。如果自上次以来有一个依赖项已更改,则效果将再次运行。(它也仍将在初始渲染后运行)

const [value, setValue] = useState('initial');

useEffect(() => {
  // This effect uses the `value` variable,
  // so it "depends on" `value`.
  console.log(value);
}, [value])

为了更好的理解useEffect,以下是涉及到的技术相关内容:


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接