3

I am currently changing my app files from class with lifecycle events such as componentDidMount to functions with useEffect hooks.

For most files I haven't seen any issues but on the below, I am getting a performance issue, where the app freezes. Zero errors or warnings in the console but my machine and Chrome increases memory used on this tab.

What have I missed?

Old Class based file code

listener: any;

componentDidMount() {
  const { firebase } = this.props;
  this.listener = firebase.onAuthUserListener(
    (authUser: any) => {
      localStorage.setItem('authUser', JSON.stringify(authUser));
      this.setState({ authUser });
    },
    () => {
      localStorage.removeItem('authUser');
      this.setState({ authUser: null });
    }
  );
}

componentWillUnmount() {
  this.listener();
}

New with hooks (causing performance issues)

const listener = () => {
  firebase.onAuthUserListener(
    (authUser: any) => {
      localStorage.setItem('authUser', JSON.stringify(authUser));
      setState(authUser);
    },
    () => {
      localStorage.removeItem('authUser');
      setState(null);
    }
  );
};

useEffect(() => {
  listener();
  return () => {
    listener();
  };
});

It may also be worth noting that I am using TypeScript with React.

1
  • Whoops, I didn't. Bad case of copy/paste from my original code. Commented Oct 19, 2019 at 3:32

3 Answers 3

2

onAuthUserListener returns a function to unsubscribe. This should be used when component unmounts.

In your code you are not returning the unsubscribe function.

const listener = () => {
  firebase.onAuthUserListener(..)    // problem here no return
}

So under the useEffect you should assign it properly and use it in useEffect's return.

const [user, setUser] = React.useState(null);

useEffect(
  () => {
    //             v------ proper assignment.
    const listener = firebase.onAuthUserListener(
      (authUser: any) => {
        localStorage.setItem('authUser', JSON.stringify(authUser));
        setUser(authUser);
      },
      () => {
        localStorage.removeItem('authUser');
        setUser(null);
      }
    );

    return () => listener();
  }
  , [] // no deps
);
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Joseph. This is what worked best for me. However, I have added firebase to the dependancies.
1

Here might be the error:

useEffect(() => {
  listener();
  return () => {
    listener();  <--- here
  };
});

The question is, what state does need to change in order to trigger a listener(); call?

I think this:

const listener = () => {
  firebase.onAuthUserListener(
    (authUser: any) => {
      localStorage.setItem('authUser', JSON.stringify(authUser));
      setState(authUser);
    },
    () => {
      localStorage.removeItem('authUser');
      setState(null);
    }
  );
};

Should be transformed into a React Hook. Cheers!

Comments

0

useEffect needs a dependency array as second argument, otherwise it keeps running on each render. Since you are updating state, this just keeps repeating

useEffect(() => {
  listener();
  return () => {
    listener();
  };
}, []);

Docs

You can use tools like Runaway Effects or ESLint for static analysis for figuring these out beforehand.

4 Comments

Thank you. That was my original thought, however I get thrown this warning Line 37:8: React Hook useEffect has a missing dependency: 'listener'. Either include it or remove the dependency array react-hooks/exhaustive-deps
If I add listener to the dependancy I get The 'listener' function makes the dependencies of useEffect Hook (at line 37) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'listener' definition into its own useCallback() Hook react-hooks/exhaustive-deps
ESLint figures that everything used inside useEffect is something that can change and introduce bugs, just add listener to the deps array, you can find more info in docs
Yes, that is what I have decided upon. The reason was listener was being reused and pulled from another file. Adding it inside of useEffect and removing the dependancy seems to have worked. Just testing now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.