【问题标题】:useEffect re-renders too many timesuseEffect 重新渲染太多次
【发布时间】:2020-04-23 15:24:57
【问题描述】:

我有这个组件,它需要获取数据,将其设置为状态,然后将其传递给孩子。 一些数据也需要在上下文中设置。 我的问题是使用 useEffect,一旦调用 API,它将为我需要执行的每个 setvalue() 函数重新渲染。 我尝试将一个空的 [] 数组传递给 useEffect,但由于状态正在发生变化,因此仍然获得相同数量的重新渲染。 目前该数组包含 set...functions 以防止 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,
  ]);

...```




【问题讨论】:

  • stackoverflow.com/a/53048903/9787887 如果状态更改是异步触发的(例如包装在 promise 中),它们将不会被批处理;。您可能在async 函数中使用了过多的set,每个set 都会导致您重新渲染,这太多。不知道你说的too much是指too much还是infinite
  • 如果API.getOpportunity()API.getUserProfile()API.getUserProfileExt()3个独立的api调用,表示结果会异步出来,那么没关系 为 3 次 api 调用 (但在你的可能是 8 次) 重新渲染 3 次(每个结果的每个重新渲染都有)?你的意思是太多的重新渲染是多少?
  • 有一些问题:如果setUserNamesetUserLastNamesetUserEmail总是被设置在一起,你应该只使用一个名为user的状态对象 而不是 3 个状态变量 userNameuserLastNameuserEmail,在这种情况下,它将为您节省 2 次重新渲染。另一个问题是您使用 await 的方式会导致 3 个 api 调用同步运行而不是并行运行,它不会导致更多的重新渲染,但会花费您更多的性能,修复您需要 Promise.all
  • @LoiNguyenHuynh 谢谢你,这些是我要实施的非常宝贵的建议。

标签: reactjs


【解决方案1】:

只将一个空数组传递给useEffect 的第二个参数。

注意

React 保证 setState 函数的身份是稳定的,不会 更改重新渲染。这就是为什么从 useEffect 中省略是安全的 或 useCallback 依赖列表。 Source

编辑:尝试this 以避免重新渲染。谨慎使用

【讨论】:

  • 貌似依赖列表中的set... 是多余的,但不会导致重新渲染?
  • 我已经尝试清除数组,但我仍然收到相同数量的重新渲染。我已经在依赖项中列出了该集合以清除来自 react-hooks/exhaustive-deps 的 ESLINT 警告
  • @Loi 你说得对,我误解了问题
  • @LoiNguyenHuynh 我一共得到了 10 次重新渲染
【解决方案2】:

仅在装载和卸载时运行

您可以传递空数组[] 的特殊值作为“仅在挂载和卸载时运行”的一种说法。因此,如果我们将上面的组件更改为像这样调用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

【讨论】:

    猜你喜欢
    • 2020-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多