Reputation: 11
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
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
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
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