2

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/>
        }
    }

}
Marlow
  • 21
  • 1
  • 1
  • 3
  • 1
    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 Jun 09 '18 at 14:10
  • Thanks for the answer! Updated the component with your suggestions. Original warning is still there ;( – Marlow Jun 09 '18 at 14:20
  • `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 Jun 09 '18 at 14:56
  • @radarbob was thinking in that way to! any suggestions for a solution? Trying to find a solution myself to – Marlow Jun 09 '18 at 15:22
  • 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()`](https://reactjs.org/docs/faq-state.html#why-is-setstate-giving-me-the-wrong-value) that takes a function; because `setState()` is fundamentally asynchronous – radarbob Jun 09 '18 at 18:59
  • Possible duplicate of [React warning about setState in unmounted component](https://stackoverflow.com/questions/50029468/react-warning-about-setstate-in-unmounted-component) – fkoessler Sep 12 '18 at 12:24

6 Answers6

2

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...

Ali SabziNezhad
  • 3,050
  • 1
  • 17
  • 31
  • Thanks for the quick answer! Tried it out but getting the same warning in the original question – Marlow Jun 09 '18 at 13:43
  • 4
    As a note, don't call async/await functions in the constructor. The constructor should initialize the component and return it as soon as possible. If you need to await an async function, call it in `componentDidMount`. – Jayce444 Jun 09 '18 at 14:06
0

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

gatlanticus
  • 1,158
  • 2
  • 14
  • 28
san
  • 1,515
  • 11
  • 18
0

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/>
        }
    }

}
Hoàng Vũ Anh
  • 647
  • 1
  • 8
  • 24
0

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

Cancel all the async operation is one of the solution

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Shawn Wang
  • 2,314
  • 1
  • 16
  • 19
-1

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

Weijing Jay Lin
  • 2,991
  • 6
  • 33
  • 53