2

While trying to perform signup I am getting this warning:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method. at SignUp (http://localhost:3000/static/js/main.chunk.js:4258:5) at div at SignInAndSignUpPage

The code in my Signup page is as follows:

import React from 'react';

import FormInput from '../form-input/form-input.component';
import CustomButton from '../custom-button/custom-button.component';

import { auth, createUserProfileDocument } from '../../firebase/firebase.utils';

import './sign-up.styles.scss';

class SignUp extends React.Component {
  constructor() {
    super();

    this.state = {
      displayName: '',
      email: '',
      password: '',
      confirmPassword: ''
    };
  }

  handleSubmit = async event => {

    event.preventDefault();

    const { displayName, email, password, confirmPassword } = this.state;

    if (password !== confirmPassword) {
      alert("passwords don't match");
      return;
    }

    try {
      const { user } = await auth.createUserWithEmailAndPassword(
        email,
        password
      );

      await createUserProfileDocument(user, { displayName });

      this.setState({
        displayName: '',
        email: '',
        password: '',
        confirmPassword: ''
      });
    } catch (error) {
      console.error(error);
    }
  };

  handleChange = event => {
    const { name, value } = event.target;

    this.setState({ [name]: value });
  };

  render() {
    const { displayName, email, password, confirmPassword } = this.state;
    return (
      <div className='sign-up'>
        <h2 className='title'>I do not have a account</h2>
        <span>Sign up with your email and password</span>
        <form className='sign-up-form' onSubmit={this.handleSubmit}>
          <FormInput
            type='text'
            name='displayName'
            value={displayName}
            onChange={this.handleChange}
            label='Display Name'
            required
          />
          <FormInput
            type='email'
            name='email'
            value={email}
            onChange={this.handleChange}
            label='Email'
            required
          />
          <FormInput
            type='password'
            name='password'
            value={password}
            onChange={this.handleChange}
            label='Password'
            required
          />
          <FormInput
            type='password'
            name='confirmPassword'
            value={confirmPassword}
            onChange={this.handleChange}
            label='Confirm Password'
            required
          />
          <CustomButton type='submit'>SIGN UP</CustomButton>
        </form>
      </div>
    );
  }
}

export default SignUp;

And the code in SignInAndSignUpPage is as follows:

import React from 'react';

import SignIn from '../../components/sign-in/sign-in.component';
import SignUp from '../../components/sign-up/sign-up.component';

import './sign-in-and-sign-up page.styles.scss';

const SignInAndSignUpPage=()=>(
 <div className='sign-in-and-sign-up page'>
    <SignIn />
    <SignUp/>
 </div>
);

export default SignInAndSignUpPage;

My complete code can be found at https://github.com/harsh0623/crwn-clothing

Please help me find the cause of this warning.

2 Answers2

0

The issue is likely that a successful createUserProfileDocument() causes a redirect here: https://github.com/harsh0623/crwn-clothing/blob/main/src/App.js#L58-L60

To mitigate this, you could remove the call to this.setState after the await.

handleSubmit = async event => {

    event.preventDefault();

    const { displayName, email, password, confirmPassword } = this.state;

    if (password !== confirmPassword) {
      alert("passwords don't match");
      return;
    }

    try {
      const { user } = await auth.createUserWithEmailAndPassword(
        email,
        password
      );

      await createUserProfileDocument(user, { displayName });
    } catch (error) {
      console.error(error);
      // Upon error, it may make sense to reset some information here instead, e.g.
      this.setState({
        password: '', 
        confirmPassword: '',
      });
    }
  };

If you have a similar reset in your SignIn component, try removing that as well for the "Sign In" path.

segFault
  • 3,887
  • 1
  • 19
  • 31
0

If the component gets unmounted before the await finishes, you will be trying to update the state on an unmounted component, which is not allowed.

To avoid this, you should keep track yourself of whether the component is mounted or not, and prevent calling setState if the component is not mounted anymore.

Add this to keep track of the mounted state:

componentDidMount() { 
  this._isMounted = true;
}

componentWillUnmount() {
   this._isMounted = false;
}

And then check if the component is still mounted when the async operation finishes:

handleSubmit = async event => {

    event.preventDefault();

    const { displayName, email, password, confirmPassword } = this.state;

    if (password !== confirmPassword) {
      alert("passwords don't match");
      return;
    }

    try {
      const { user } = await auth.createUserWithEmailAndPassword(
        email,
        password
      );

      await createUserProfileDocument(user, { displayName });

      if(this._isMounted) {
          this.setState({
            displayName: '',
            email: '',
            password: '',
            confirmPassword: ''
          });
      }
    } catch (error) {
      console.error(error);
    }
  };

For more info on how to do it using functional components and hooks, check this other answer.

dglozano
  • 6,369
  • 2
  • 19
  • 38