Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React warning about setState in unmounted component

Tags:

reactjs

I'm getting this error:

warning.js:33 Warning: Can't call setState (or forceUpdate) 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.

But I'm not using a componentWillUnMount method.

I'm using a HOC to make sure the user is authenticated before accessing their /account route.

Here's the Route:

<StyleRoute props={this.props} path="/account" component=  {RequireAuth(Account)} /> 

where RequireAuth is the HOC. Here's the HOC:

 import { withRouter } from 'react-router';  export default function RequireAuth(Component) {    return class AuthenticatedComponent extends React.Component {      componentWillMount() {       this.checkAuth();     }      checkAuth() {       if ( ! this.props.isAuthenticated) {         this.props.history.push(`/`);       }     }      render() {       return this.props.isAuthenticated         ? <Component { ...this.props } />         : null;     }    }    return withRouter(AuthenticatedComponent); } 

The code works as intended, but I'm getting that error when /account is rendered. As you notice, nowhere in my direct code is there an componentWillUnMount method. I'm really at a loss for why this warning keeps popping up and any info would help.


Update 5/23/18:

To get rid of the error and still have props pass down, I did two thing:

1) I opted for a having two higher order functions in parent App component instead of using the HOC. One higher order function is for passing props and the other is to check authentication. I was having trouble passing any props other than the browser history, hence the renderProps function below.

renderProps = (Component, props) => {   return (       <Component {...props} />     ); }  checkAuth = (Component, props) => {     if (props.isAuthenticated) {         return <Component {...props} />     }     if (!props.isAuthenticated) {         return <Redirect to='/' />     } } 

2) To use these, I had to user render in my Route, as opposed to component.

//I could pass props doing this, sending them through the above functions <Route exact path="/sitter-dashboard" render={ () => this.checkAuth(SitterDashboard, this.props) } /> <Route exact path={"/account/user"} render={() => this.renderProps(User, this.props)} />  //I couldn't pass props doing this <Route {...this.props} exact path="/messages" component={Messages} /> 

Here's the documentation on router vs component as a Route render method: https://reacttraining.com/react-router/web/api/Route/route-render-methods

Also, here's a good explanation on Stack Overflow

Finally, I used this code from the React Router 4 documentation as a template for what I did above. I'm sure the below is cleaner, but I'm still learning and what I did makes a bit more sense to me.

const PrivateRoute = ({ component: Component, ...rest }) => ( <Route   {...rest}   render={props =>   fakeAuth.isAuthenticated ? (        <Component {...props} />       ) : (         <Redirect           to={{             pathname: "/login",             state: { from: props.location }           }}         />       )     }   /> ); 
like image 387
Cameron Avatar asked Apr 25 '18 18:04

Cameron


People also ask

Can set state on unmounted component?

Seeing called setState() on an unmounted component in your browser console means the callback for an async operation is still running after a component's removed from the DOM. This points to a memory leak caused by doing redundant work which the user will never benefit from.

How do you find warning Cannot perform a React state update on an unmounted component?

To solve the "Warning: Can't perform a React state update on an unmounted component", declare an isMounted boolean in your useEffect hook that is used to track whether the component is mounted. A component's state should only be updated if the component is mounted.

Can't call setState or forceUpdate on an unmounted component This is a no op?

Warning: Can't call setState (or forceUpdate) 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.

Can perform a React state update on an unmounted component?

In the happy path you won't have any issues — setState will execute, and your state will successfully update. But if you invoke setState within the context of an asynchronous operation, then you might run into the React warning “Can't perform a React state update on an unmounted component”.


2 Answers

I had the same error time ago and it was generated by a component which was using ref tag, and there was some manual manipulation.

A good practice to see these kind of errors is drawing your app flow and see when your are calling setState.

Another thing I would change if I were you is componentDidMount instead of componentWillMount to check some data. Take into account fb deprecated this functionality.

This lifecycle was previously named componentWillMount. That name will continue to work until version 17. Use the rename-unsafe-lifecycles codemod to automatically update your components.

Reactjs component documentation

like image 84
Lucas Milotich Avatar answered Nov 03 '22 00:11

Lucas Milotich


I had a similar problem, but I did figure out the reason behind the same, so here is the snippet of code where I was encountering this err.

Warning: Can't call setState (or forceUpdate) 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.  

Cause:

   this.setState({ showLoader: true });     const { username } = this.state;     const URL = `https://api.github.com/users/${username}`;     try {       const { data } = await axios(URL);       this.props.apiData(data);       this.props.history.push("profile");     } catch (e) {       console.error(e);     }     this.setState({ showLoader: false }); 

As you can see in the code-snippet, I was doing

this.props.history.push("profile"); 

before setting the state.

this.setState({ showLoader: false }); 

And then err seems to be legit in this case as I was redirecting to a different component and then setting the state on the component I was earlier.

Solution:

By placing

this.setState({ showLoader: false }); 

above the this.props.history.push("profile"); solved the problem.

I hope this helps.

like image 33
Divyanshu Rawat Avatar answered Nov 02 '22 23:11

Divyanshu Rawat