Home > Software design >  dispatch is called 3 times when entered capital letters
dispatch is called 3 times when entered capital letters

Time:11-27

I am creating a gmail clone with mern and am using socket io to make it realtime aswell as redux for state management

Now when the user creates a mail I listen to it like this

  useEffect(() => {
    socket &&
      socket.on("recieveMail", ({ mail }: any) => {
        const localActiveTab = activeTab;
        if (mail.mailType === localActiveTab) {
          dispatch(addMail(mail));
        }
      });
  }, [socket, dispatch, activeTab]);

addMail

    addMail: (state, { payload }) => {
      if (state.activeTab === "primary") {
        state.primaryMails.splice(0, 0, payload);
      } else if (state.activeTab === "promotions") {
        state.promotionMails.splice(0, 0, payload);
      } else {
        state.socialMails.splice(0, 0, payload);
      }
    },

initialstate

const initialState: initialStateTypes = {
  error: "",
  primaryMails: [],
  promotionMails: [],
  socialMails: [],
  activeTab: "primary",
  isLoading: false,
};

And this works fine, when the user creates a mail it shows said mail only one time but when the user enters capital letters in the subject and the body (I think 2 capitals on body) then it shows that mail 3 times.

Does anyone know why this is happening and how I can fix it?

CodePudding user response:

Quick fix

 useEffect(() => {

    let isValidScope = true;

    socket &&
      socket.on("recieveMail", ({ mail }: any) => {

        // if message received when component unmounts
        // stop setting state in scope previous render cycle
        if (!isValidScope) { return; }

        const localActiveTab = activeTab;
        if (mail.mailType === localActiveTab) {
          dispatch(addMail(mail));
        }
      });

      return () => {
             isValidScope = false;
             // disconnect code
            // socket.disconnect()
      }

  }, [socket, dispatch, activeTab]);

Slighty better maintainable solution

  const activeTabRef = useRef(activeTab);
  activeTabRef.current = activeTab;

   useEffect(() => {

        let isValidScope = true;

        socket &&
          socket.on("recieveMail", ({ mail }: any) => {

            // if message received when component unmounts
            // stop setting state in scope previous render cycle
            if (!isValidScope) { return; }

            // the socket connect/reconnect will trigger when
            // activeTab state changes, if you dont desire that
            // use a useRef to refer to latest value in useEffect event handler
            const localActiveTab = activeTabRef.current;
            if (mail.mailType === localActiveTab) {
              dispatch(addMail(mail));
            }
          });

          return () => {
                 isValidScope = false;
                 // disconnect code
                // socket.disconnect()
          }

      }, [socket]);
     // dispatch is generally stable reference, can be skipped from depedency array
     // if dispatch is not stable, try using a reference with useRef
     // remove activeTab, if you dont connect/reconnect on activeTab change
     

more about useEffect life cycle, to get an idea why

  • A new effect is created after every render
  • How the cleanup for previous effect occurs before executing of current useEffect

You can read about why isValid is set synchronizing with effects

Why it was running 3 times in dev mode

If you are intererested in taking a deep dive, consider reading a blog post by Dan on useEffect, its old but explanation the details to build a good mental model about useEffects and functional components.

useEvent can solve the problem but it is in RFC

you can check my answer about a implementation to build a custom useEvent till it becomes stable

Hope it helps, cheers

  • Related