2
votes

Keep getting the following error message in React Native, really don't understand where it is coming from

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.

I have the following simple component:

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            isLoggedIn: false,
        } 
    }

    componentDidMount(){
        this.fetchToken()
      }

    async fetchToken(){
        const access_token = await AsyncStorage.getItem('access_token')
        if (access_token !== null) {
           this.setState({ isLoggedIn: true })
        }
    }

    render() {
        const login = this.state.isLoggedIn
        if (login) {
            return <NavigatorLoggedIn />
        } else { 
            return <Navigator/>
        }
    }

}
6
You are supposed to initialize the state within the constructor, not outside it. And use this.state, not just state. I also wouldn't apply the async attribute to React's default lifecycle methods, best leave them as they are. Create a separate async/await component function for doing that, then call it in componentDidMount - Jayce444
Thanks for the answer! Updated the component with your suggestions. Original warning is still there ;( - Marlow
componentDidMount() runs after rendering so updated state is not seen/available in render(). Then I suppose this component gets unmounted via render() in any case and so calling fetchToken() after the fact causes a memory leak, per the error messgage. - radarbob
@radarbob was thinking in that way to! any suggestions for a solution? Trying to find a solution myself to - Marlow
off hand: (1) do it in render() at the very top. (2) do it in the constructor. In either case use the other form of setState() that takes a function; because setState() is fundamentally asynchronous - radarbob

6 Answers

2
votes

You can use it:

componentDidMount() {
    this.function()
}

function = async () => { 
    const access_token = await AsyncStorage.getItem('access_token')
    if (access_token !== null) {
        this.setState({ isLoggedIn: true })
    }
}

Or you can call function in constructor.

I hope this will help you...

0
votes

It's will be work:

let self;

class App extends React.Component {
    constructor(props) {
        super(props)
        self = this;
        this.state = {
            isLoggedIn: false,
        } 
    }

    componentDidMount(){
        this.fetchToken()
      }

    async fetchToken(){
        const access_token = await AsyncStorage.getItem('access_token')
        if (access_token !== null) {
           self.setState({ isLoggedIn: true })
        }
    }

    render() {
        const login = self.state.isLoggedIn
        if (login) {
            return <NavigatorLoggedIn />
        } else { 
            return <Navigator/>
        }
    }

}
0
votes

you need use isMounted variable.

componentDidMount(){
  this.setState({ isMounted = true });
  const access_token = await AsyncStorage.getItem('access_token')
  if (access_token !== null && this.isMounted) {
     this.setState({ isLoggedIn: true })
  }
}

componentWillUnmount(){
   this.setState({ isMounted = false });
}

Or if you use Axios, you can use cancel request feature of axios this here: https://github.com/axios/axios#cancellation

0
votes

You can try this:

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            isLoggedIn: false,
        } 
    }

    _isMounted = false;   

    componentDidMount(){
        this._isMounted = true; 
        this.fetchToken()
      }

    async fetchToken(){
        const access_token = await AsyncStorage.getItem('access_token')
        if (access_token !== null && this._isMounted) {
           this.setState({ isLoggedIn: true })
        }
    }

    componentWillUnmount() {
        this._isMounted = false;
    } 

    render() {
        const login = this.state.isLoggedIn
        if (login) {
            return <NavigatorLoggedIn />
        } else { 
            return <Navigator/>
        }
    }

}

By using _isMounted, setState is called only if component is mounted, The unmounting doesn't wait for the async call to finish. Eventually when the async call gets over, the component is already unmounted and so it cannot set the state. To do this, the answer simply does a check to see if the component is mounted before setting the state.

0
votes

Cancel all the async operation is one of the solution

-1
votes

For me, I resolved it by restart the server by "yarn start" or "npm start"