0
votes

I am trying the new hooks features and stuck upon my state not being updated.

Actually the state is updated ( I can see the updated values in the console.log and my component reruns the useEffect) but the useEffect method uses my old state and save the signature to only the first user while The active user did change in the state.

I thought about adding useCallback and to move my methods to the useEffect or the component itself but I could manage to get it to work.

state:

const inititalState = {
  displayHolder: true,
  activeUser: 0,
  signatureDate: new Date().toLocaleDateString('he'),
};

this is my useEffect:

useEffect(() => {
    try {
        ...
        canvas._canvas.addEventListener('mousedown', throttle(() => handleSignatureStart(dispatch), 500));
        canvas._canvas.addEventListener(
            'mouseup',
            throttle(() => handleSignatureEnd(activatedUser, name, canvas, onFinish), 500),
        );

        ...

        // set to new signature and date
        if (signature && typeof signature === 'string') {
            dispatch({ type: 'setSignatureDetails', payload: { displayHolder: false, signatureDate } });
            canvas.fromDataURL(signature);
        } else {
            dispatch({
                type: 'clearSignatureDetails',
                payload: { displayHolder: true, signatureDate: new Date().toLocaleDateString('he') },
            });
        }

        //umount
        return () => {
            if (canvas) {
                canvas._canvas.removeEventListener('mousedown', handleSignatureStart);
                canvas._canvas.removeEventListener('mouseup', handleSignatureEnd);
            }
        };
    } catch (error) {
        console.error(error);
    }
}, [dispatch]);

here is my reducer:

const signatureReducer = (state, { type, payload }) => {
    console.log('@@@@', type, '@@@');
    switch (type) {
        case 'setSignatureDetails': {
            const { displayHolder, signatureDate, activeUser } = payload;
            const activeUserReady = activeUser === 0 || activeUser ? activeUser : state.activeUser;
            console.log(activeUser, activeUserReady);
            return { ...state, displayHolder, signatureDate, activeUser: activeUserReady };
        }
        case 'clearSignatureDetails': {
            const { displayHolder, signatureDate } = payload;
            return { ...state, displayHolder, signatureDate };
        }
        case 'hidePlaceholder': {
            const { displayHolder } = payload;
            return { ...state, displayHolder };
        }
        case 'showPlaceholder': {
            const { displayHolder, activeUser } = payload;
            const activeUserReady = activeUser === 0 || activeUser ? activeUser : state.activeUser;
            console.log(activeUser, activeUserReady);
            return { ...state, displayHolder, activeUser: activeUserReady };
        }
        default:
            return state;
    }
};

parent state:

  users: [
    {
      name: 'foo',
      forwhom: 0,
      signatures: {
        clientSignature: {
          value: ''
        }
      }
    },
    {
      name: 'bar',
      forwhom: 1,
      signatures: {
        clientSignature: {
          value: ''
        }
      }
    }
  ]

Here is that part of my project which possibly demonstrates my issue. https://stackblitz.com/edit/react-51j5i6

Thanks

1
That's a lot of code. Please try and create a Minimal, Complete, and Verifiable example directly in the question instead. You need to give all effect dependencies in the second argument to useEffect, or it will not re-run with the correct values. - Tholle
Thank you, I will try to reduce the code further but it is necessary to reproduce the example. I did pass dispatch as the second argument to useEffect as stated in the articles and the docs. isnt it right? - Kiper
Your useEffect leverages state and props, but does not have them in the dependencies array. It is also very confusing to see the state and props being deconstructed both outside and inside useEffect with the same variable names. - Ryan Cogswell
I read here -> "overreacted.io/a-complete-guide-to-useeffect" that I should have all my variables inside my useEffect but I do need them outside of it too, so is it okay they are duplicated and i should change the names only? - Kiper
@RyanCogswell Actually I have added the lint rules to help me with that and it did add props and state to the second argument. but then my component started to rerender infinitly. - Kiper

1 Answers

0
votes

It's difficult to see what your desired state should be from your demo. But, from what I understand, I believe your useEffect dependencies should be like this:

useEffect(() => {
  ...
}, [activeUser]);

A couple other things; as @Ryan Cogswell pointed out, you do not need to redefine activeUser inside the useEffect callback.

Also, inside your useEffect callback; if you intend to run this function on update to the active user (IE multiple times), you should either remove the event listeners or check if the listeners have been added to prevent adding event listeners each render.