react-top-loading-bar Warning: Can't perform a React state update on an unmounted component

Viewed 1107

I wanted to add feature for a website with progress bar on top. And installed dependency "react-top-loading-bar". It worked, but a warning occurred.

P.S. I HAVE already reviewed other similar topics, but it did not work out for me

import React, {Component} from "react";
import {connect} from "react-redux";
import {auth} from "../store/actions/user";
import Loader from "../UI/Preloader/loader";
import LoadingBar from "react-top-loading-bar";


// Auth(Component, reload)

// This is an HOC component, on every route change I check if user is authenticated

// reload true means that if user is not authenticated it will be redirected to login page
// if reload is false it means user is already logged in and redirected to his dashboard
// if there is no reload user can access those pages without authentication

export default function (ComposedClass, reload) {

   class AuthenticationCheck extends Component {

      _isMounted = false;
      
      state = {
         loading: true,
         progress: 0
      };

      //// checking if user is authenticated
      componentDidMount() {
         window.scrollTo(0, 0);

         this._isMounted = true;
         
         if(this._isMounted) {
            setTimeout(() => {
               this.setState({
                  progress: 100
               })
            }, 300);
         }


         if (this.props.user.authLogin && this.props.user.authLogin.id) {
            this.setState({loading: false});
         } else {
            this.props.dispatch(auth());
         }
      }

      UNSAFE_componentWillReceiveProps(nextProps) {

         this.setState({loading: false});

         if (!nextProps.user.authLogin.isAuth) {
            if (reload) {
               this.props.history.push("/login");
            }
         } else {
            if (reload === false) {
               this.props.history.push("/profile")
            }
         }
      }

      componentWillUnmount() {
         this._isMounted = false;
      }


      render() {
         if (this.state.loading) {
            return <Loader/>
         }
         return (
             <React.Fragment>
                <LoadingBar
                    color='#45C0AE'
                    height={4}
                    progress={this.state.progress}
                    // loaderSpeed={1000}
                    onLoaderFinished={() => {}}
                />
                <ComposedClass {...this.props} user={this.props.user}/>
             </React.Fragment>
         )
      }
   }

   function mapStateToProps(state) {
      return {
         user: state.user_r
      }
   }

   return connect(mapStateToProps)(AuthenticationCheck);
}

I tried using above method with (_ismounted), but it did not work for me.

Warning happens if I change routes quickly at the same time, before progress bar could finish to reach 100%. But if I wait 1-2 seconds until progress bar finishes, and then change a route - in this case there was no warning.

2 Answers

You have to add the check also to UNSAFE_componentWillReceiveProps, because that will be called too but the component is already gone.

  UNSAFE_componentWillReceiveProps(nextProps) {
         if(!this._isMounted){return;}
         this.setState({loading: false});

         if (!nextProps.user.authLogin.isAuth) {
            if (reload) {
               this.props.history.push("/login");
            }
         } else {
            if (reload === false) {
               this.props.history.push("/profile")
            }
         }
      }

      componentWillUnmount() {
         this._isMounted = false;
      }

I would also recommend to use function components to use the newer better ways to render this. Also there is a reason not to use UNSAFE_componentWillReceiveProps. Try also instead componentDidUpdate.

It appears that the problem is with "react-top-loading-bar". So by changing some properties of Loading bar from documentation: https://www.npmjs.com/package/react-top-loading-bar Did help me a little bit, by reducing transitionTime, loaderSped and waitingTime from its default values

<LoadingBar
     transitionTime={200} // default 300
     loaderSpeed={400}    // default 400
     waitingTime={800}    // default 1000
     progress={this.state.progress}
/>

From now on I see lesser warnings. If you reduce waitingTime and loaderSpeed even more -> Your loading bar will become too fast, but warning will disappear.

Related