0

I get the Warning "Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op" at following code. At GitHub you can find the full working react-navigation example with firebase and Redux. I think the problem could be seen if a more skilled person than me looks at the code.

I not know how to compensate this warning, when i jump beck from "roles2" to the "home" navigation. Could you find the bug?

import React, { Component } from "react";
import { Text, View, TouchableHighlight, TouchableOpacity, ListView, StatusBar, ScrollView, StyleSheet } from "react-native";
import Styles from "./../../App.scss";
import Firebase from "./../Util/database";
import Item from "./item";
const Uuid = require('uuid/v1');
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import { NavigationActions, SafeAreaView } from "react-navigation";
import { connect } from "react-redux";

var db = Firebase.firestore();

const list = ['Loading...']

export default class Empty extends Component {

constructor(props) {
  super(props);
  this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})

  this.state = {
    dataSource: this.ds.cloneWithRows(list),
  };

  this.roles = db.collection("roles").get();
  this.renderItem = this.renderItem.bind(this)
  this.setItemsFromFirestore = this.setItemsFromFirestore.bind(this);
}

setItemsFromFirestore(roles) {
  roles.then((querySnapshot) => {
  // get children as an array
  var items = [];
  querySnapshot.forEach((child) => {
    items.push({
      label: `${child.data().label}`,
      description: `${child.data().description}`,
      id: `${child.id}`
    });
  });

  this.setState({
    dataSource: this.ds.cloneWithRows(items),
  });
});
}

componentWillUpdate(nextProps, nextState) {
  this.roles = db.collection("roles").get();
  this.setItemsFromFirestore(this.roles);
}

componentDidMount() {
  this.setItemsFromFirestore(this.roles);
}

renderItem(item, navigation) {
  return (
   <Item key={Uuid()} label={item.label} description={item.description} onPress={ () => {
    const param = item.param;
    const route = item.route;
    const navigateAction = NavigationActions.navigate({
      routeName: 'role',
      params: {
        label: `${item.label}`,
        description: `${item.description}`,
        id: `${item.id}`
      }
    });
    navigation.dispatch(navigateAction);
  }} />
 )
}

static navigationOptions = props => {
  const { navigation } = props;
  const { state, setParams } = navigation;
  const { params } = state;
  return {
  title: "Rollen (Cloud)",
  headerTintColor: Styles.ci_Header.color,
  headerStyle: {
    height: Styles.ci_Header.height,
    backgroundColor: Styles.ci_Header.backgroundColor
  },
  headerRight: (
    <FontAwesome
      name= {'plus'}
      size={18}
      style={{ color: Styles.ci_Header.color, paddingHorizontal: 5}}
      onPress={() => {
        const id = `${Uuid()}`;
        var data = {
          id: {id},
          label: `Neue Rolle (${id.substring(0,6)}...)`,
          description: ''
        };
        var setDoc = db.collection('roles').doc(id).set(data);
      }}
    />
  )
 };
};

render() {
 return (
  <View style={{ flex: 1 }}>
    <ListView
        dataSource={this.state.dataSource}
        renderRow={item => this.renderItem(item, this.props.navigation)} />
    <StatusBar barStyle="light-content" />
  </View>
  );
 }
}

I am new to react-native with navigation and did not get what i did wrong.

Could you find the bug?

Matthias Wegner
  • 303
  • 4
  • 18
  • You might find this useful https://stackoverflow.com/questions/39767482/is-there-a-way-to-check-if-the-react-component-is-unmounted/39767963#39767963 – Shubham Khatri Jan 30 '18 at 11:12

2 Answers2

2

This error means you are calling the setState method even after the component is unmounted. This is usually caused by the asynchronous API call. So your API call returned result after you have moved to a different page. To avoid this warning, you can set a variable to true in the component class in componentDidMount and check if that variable has true value before calling the setState method.

componentDidMount(){
  this.isMounted = true;
}

fetchData(){
 if(this.isMounted){
  this.setState();
 }
}
Sibaprasad Maiti
  • 507
  • 8
  • 19
0

I found a solution, but am not satisfied. I use

this.mounted = false;
this.state = {
  isMounted: true,
  ... 
};

On unmount i use

componentWillUnmount() {
   this.mounted = false;
   this.setState( {isMounted: false} );
   console.log('Will Unmount '+componentName);
}

And now watch. On componentWillUpdate i log

 console.log("mounted?: "+this.state.isMounted +" (isMounted-Stat) - "+this.mounted+" (mounted)");

and the console throws

13:28:53: Will Update roles2
13:28:53: Will Unmount roles2
13:28:53: mounted?: true (isMounted-Stat) - false (mounted)

That means i can use this.mounted when i define it as parameter inside the component, but the State is NOT updated with Unmount. This is not logical in my point of view, but i live with it. With this solution (constructor, componentWillUnmount, componentWillUpdate) it works and the warning ist gone.

I really like to prefer a better way, because the warning is throws when the component was left AFTER the Unmount. So somewhere the function setState is called even if the component was left an unmounted. And when i call setState in unmount the parameter is NOT set.

Matthias Wegner
  • 303
  • 4
  • 18