Yousef Alsbaihi
Yousef Alsbaihi

Reputation: 11

Can't call setState (or forceUpdate)

I'm seeing this error in the console and tried a lot of solutions nothing work

ExceptionsManager.js:84 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. in Start (at SceneView.js:9) in SceneView (at SwitchView.js:12) in SwitchView (at createNavigator.js:57) in Navigator (at createNavigationContainer.js:376) in NavigationContainer (at App.js:95)

I tried these solutions and a lot others Link Link Link Link

App.js

import React from 'react';
import { Platform, StatusBar, Image } from 'react-native';
import { AppLoading, Asset } from 'expo';
import AppPreLoader from './components/AppPreLoader'
import { Block, GalioProvider } from 'galio-framework';
import Screens from './navigation/Screens';
import { Images, materialTheme } from './constants/';
import firebaseConfig from './constants/Firebase';
import * as firebase from 'firebase';
import Notlogged from './navigation/Notlogged';
firebase.initializeApp(firebaseConfig);

// cache app images
const assetImages = [
  Images.Onboarding,
];

function cacheImages(images) {
  return images.map(image => {
    if (typeof image === 'string') {
      return Image.prefetch(image);
    } else {
      return Asset.fromModule(image).downloadAsync();
    }
  });
}


export default class App extends React.Component {
  constructor () {
        super();
        this.state = {
            isLogged: false,
            loaded: false,
            isReady: false,
        }
  }

  async componentDidMount () {
    await firebase.auth().onAuthStateChanged((user) => {
      if(user !== null) {
        this.setState({
          isLogged: true,
                    loaded: true
        });
      } else {
        this.setState({
          isLogged: false,
                    loaded: true
        });
      }
    })
  }

  componentWillUnmount(){
  }

  render() {

    if (!this.state.isReady) {
      return (
        <AppLoading
          startAsync={this._loadResourcesAsync}
          onFinish={() => this.setState({ isReady: true })}
          onError={console.warn}
        />
      );
    }

    const {isLogged, loaded, isReady} = this.state;
    if ( ! loaded) {
            return (
        <AppPreLoader/>
        );
        }

    if(isLogged && isReady) {
            return (
          <GalioProvider theme={materialTheme}>
            <Block flex>
              {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
              <Screens />
            </Block>
          </GalioProvider>
        );
    }else{
      return (
        <GalioProvider theme={materialTheme}>
          <Block flex>
            {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
            <Notlogged />
          </Block>
        </GalioProvider>
        );
    }

  }

  _loadResourcesAsync = async () => {
    return Promise.all([
      ...cacheImages(assetImages),
    ]);
  };

  _handleLoadingError = error => {
    console.warn(error);
  };

  _handleFinishLoading = () => {
    this.setState({ isLoadingComplete: true });
  };
}

the warning occur when I signing and the app go to home screen but if I just opened the app after login it's fine no warning the warning only after I login.

Video shows that if I'm already logged in no warning, but if I'm trying to login the warning will occur

https://streamable.com/s/yjt8x/nvgtbh

Upvotes: 1

Views: 238

Answers (3)

shubham choudhary
shubham choudhary

Reputation: 3110

  if(isLogged && isReady) {
            return (
          <GalioProvider theme={materialTheme}>
            <Block flex>
              {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
              <Screens />
            </Block>
          </GalioProvider>
        );

here, You are waiting for isLogged and isReady, not depending upon whether your async data is loaded or not. So component is getting unmounted, before loading assets and when promise to asset loading resolves, it tried to setstate, on component which is no longer mounted.

try using if with else,

Upvotes: 0

blaz
blaz

Reputation: 4068

The error is pretty clear: componentWillUnmount should not contain this.setState or any attempt to modify state. It is mentioned in React documentation:

You should not call setState() in componentWillUnmount() because the component will never be re-rendered. Once a component instance is unmounted, it will never be mounted again.

EDIT: Ok your component has 1 more problem: the handler for firebase.auth().onAuthStateChanged() might run during or after the component is unmounted, which React detects as an attempt to update state on unmounted component. To solve this, you can set a boolean value for your component to check if it still need to execute setState or not:

  async componentDidMount () {
    this.unmounted = false;
    await firebase.auth().onAuthStateChanged((user) => {
      if (this.unmounted) {
        return false;
      }
      if(user !== null) {
        this.setState({
          isLogged: true,
                    loaded: true
        });
      } else {
        this.setState({
          isLogged: false,
                    loaded: true
        });
      }
    })
  }

  componentWillUnmount(){
    this.unmounted = true;
  }

Upvotes: 3

sumit kumar pradhan
sumit kumar pradhan

Reputation: 653

componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any subscriptions that were created in componentDidMount(). You should not call setState() in componentWillUnmount() because the component will never be re-rendered. Once a component instance is unmounted, it will never be mounted again.

Upvotes: 0

Related Questions