使用自定义挂钩时的重新渲染问题

Posted

技术标签:

【中文标题】使用自定义挂钩时的重新渲染问题【英文标题】:Rerendering problem when using a custom hook 【发布时间】:2021-12-06 18:16:32 【问题描述】:

我有这个自定义钩子 useCountDown,它基本上是一个倒数计时器,我在 InputInvestment 组件上使用它em> 我使用调度将计数发送到商店并通过 useSelector 捕获此数据,我的输出类似于:

timerCountdown 10

重新渲染

timerCountdown 9

重新渲染

timerCountdown 8

重新渲染

timerCountdown 7

重新渲染

timerCountdown 6

重新渲染

.

.

.

.

如您所见,我遇到了重新渲染问题,我想在不重新渲染我的 InputInvestment 组件

的情况下获取此倒计时数据

useCountDown

        import  useEffect, useState  from 'react';
        import  useSelector  from 'react-redux';
        import  setTimerCountdown  from 'redux/actions/expirationTimer';
        import  useDispatch  from 'react-redux';
        
        const useCountDown = (start) => 
          const dispatch = useDispatch();
          const timerCountdown = useSelector(state => state.expirationTimer.countdown);
          const [counter, setCounter] = useState(start);
          useEffect(() => 
            if (counter === 0) 
              return;
            
            setTimeout(() => 
              setCounter(counter - 1);
            , 1000);
          , [counter]);
        
          dispatch(setTimerCountdown(counter));
        
          return timerCountdown;
        ;
    
    export default useCountDown;

InputInvestment 组件

    const InputInvestment = ( history, offering, userState, attemptToInvest ) => 
          const expirationTimer = 
            EXPIRATION_TIMEOUT: 15,
            EXPIRATION_INTERVAL: 1000,
          ;
        
          useCountdown(expirationTimer.EXPIRATION_TIMEOUT);
          const timerCountdown = useSelector(state => state.expirationTimer.countdown);
        
          // eslint-disable-next-line no-console
          console.log('rerender')
        
          // eslint-disable-next-line no-console
          console.log('timerCountdown', timerCountdown)

if (timerCountdown === 0) 
    const title = 'Investment request expired';
    const errorMessage = [
      <>
        Your investment request has expired due to inactivity. If you would like
        to invest in offering.title, please' '
        <Link to=`/offering/$offering.urlHash/`>click here</Link> to start a
        new investment.
      </>,
    ];
    return (
      <NotificationWrapper>
        <NotificationMessage title=title textList=errorMessage />
      </NotificationWrapper>
    );
   else 
    ...
;

【问题讨论】:

如果你不打算使用状态变量,为什么还要它? @RobertoZvjerković 到底是哪一个? timerCountdown 我是用来做if语句的,我没有贴出完整的代码。 所以贴出完整代码 【参考方案1】:

我对这个问题的解决方案是将这个倒数计时器逻辑直接放在操作中并在 InputInvestment 组件中调度它,并且我创建了一个布尔值而不是在组件中检索一个数字,所以现在不再发生重新渲染: )

动作

export const setTimerRunning = (expirationTime) => dispatch => 
      let counter = expirationTime;
    
      const interval = setInterval(() => 
        counter--;
        // eslint-disable-next-line no-console
        console.log('setTimerRunning',counter)
        if (counter < 0 ) 
          clearInterval(interval);
        
    
        if(counter === 0)
          dispatch(
            type: SET_TIMER_EXPIRED
          );
        
      , 1000);
    ;

减速器

import update from 'immutability-helper';
import * as actions from 'redux/actions/expirationTimer';

export const initialState = 
  expired: false,
;

    const expirationTimer = (state = initialState, action) => 
      switch (action.type) 
        case actions.SET_TIMER_EXPIRED: 
          return update(state, 
            expired: 
              $set: true,
            ,
          );
        
    
        default:
          return state;
      
    ;
    
    export default expirationTimer;

【讨论】:

以上是关于使用自定义挂钩时的重新渲染问题的主要内容,如果未能解决你的问题,请参考以下文章

在 WordPress 中更新自定义 PageLine 的部分时的操作挂钩

创建永不重建的自定义钩子

如何在自定义挂钩上使用 useCallback?

调用自定义挂钩时挂钩调用无效

通过 useFooController/useFooHook 限制使用 useContext 的组件的渲染

Drupal 8 自定义模块“找不到主题挂钩”