jarwin
jarwin

Reputation: 668

Rerendering problem when using a custom hook

I have this custom hook useCountDown which is basically a countdown timer and I'm using it on InputInvestment component i use a dispatch to send count to the store and catch this data via the useSelector and my output is something like:

timerCountdown 10

rerender

timerCountdown 9

rerender

timerCountdown 8

rerender

timerCountdown 7

rerender

timerCountdown 6

rerender

.

.

.

.

As you can see I'm having a rerender problem and I would like to get this countdown data without rerendering my InputInvestment component

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 component

    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 {
    ...
};

Upvotes: 0

Views: 129

Answers (1)

jarwin
jarwin

Reputation: 668

My solution to this problem was to put this countdown timer logic directly in the action and dispatch this in InputInvestment component and also I created a boolean instead of retrieving a number in the component, so now the rerendering don't happens anymore :)

Action

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);
    };

Reducer

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;

Upvotes: 1

Related Questions