【问题标题】:Warning: setState(...): Can only update a mounted or mounting component警告:setState(...):只能更新已安装或正在安装的组件
【发布时间】:2026-01-16 01:45:01
【问题描述】:

我无法摆脱这个错误。当我从数组中删除一项并更新状态时,就会发生这种情况。

经过一些调试,我发现如果我重新加载应用程序,直接进入此屏幕并删除,错误不会显示。 但是,如果我导航到此屏幕,返回,然后再次转到此屏幕并删除,则会出现错误。如果我加载屏幕 10 次,我会收到 (30) 个这样的错误。

我的猜测是,当弹出路由返回仪表板时,我没有关闭 firebase 连接。或者我使用的导航器没有正确卸载场景。或者 deleteRow() 函数有问题

我真的不知道我还能做什么。同样的问题遍布整个网络,但似乎不适用于我的场景。

  constructor(props) {
    super(props);
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
    };

    const user = firebase.auth().currentUser;
    if (user != null) {
      this.itemsRef = this.getRef().child(`Stores/${user.uid}/`);
    } else {
      Alert.alert('Error', 'error!');
    }
    this.connectedRef = firebase.database().ref('.info/connected');
  }

  componentWillMount() {
    this.connectedRef.on('value', this.handleValue);
  }

  componentWillReceiveProps() {
    this.connectedRef.on('value', this.handleValue);
  }

  componentWillUnmount() {
    this.connectedRef.off('value', this.handleValue);
  }

  /*eslint-disable */
  getRef() {
    return firebase.database().ref();
  }
  /*eslint-enable */

  handleValue = (snap) => {
    if (snap.val() === true) {
      this.listenForItems(this.itemsRef);
    } else {
      //this.offlineForItems();
    }
  };

  listenForItems(itemsRef) {
    this.itemsRef.on('value', (snap) => {
      const items = [];
      snap.forEach((child) => {
        items.unshift({
          title: child.val().title,
          _key: child.key,
        });
      });
      offline2.save('itemsS', items);
      this.setState({
        dataSource: this.state.dataSource.cloneWithRows(items),
      });
    });
  }

  offlineForItems() {
    offline2.get('itemsS').then((items) => {
      this.setState({
        dataSource: this.state.dataSource.cloneWithRows(items),
      });
    });
  }

  deleteConfirm(data, secId, rowId, rowMap) {
    Alert.alert(
        'Warning!',
        'Are you sure?',
      [
          { text: 'OK', onPress: () => this.deleteRow(data, secId, rowId, rowMap) },
          { text: 'Cancel', onPress: () => this.deleteCancel(data, secId, rowId, rowMap) },
      ]
    );
  }

  deleteCancel(data, secId, rowId, rowMap) {
    rowMap[`${secId}${rowId}`].closeRow();
  }

  deleteRow(data, secId, rowId, rowMap) {
    rowMap[`${secId}${rowId}`].closeRow();
    this.itemsRef.child(data._key).remove();
    offline2.get('itemsS').then((items) => {
      const itemsTemp = items;
      let index = -1;
      for (let i = 0; i < itemsTemp.length; i += 1) {
        if (itemsTemp[i]._key === data._key) {
          index = i;
        }
      }
      if (index > -1) {
        itemsTemp.splice(index, 1);
      }
      // Decrement stores counter
      offline2.get('storesTotal').then((value) => {
        const itemsReduce = value - 1;
        this.setState({ storesValue: itemsReduce });
        offline2.save('storesTotal', itemsReduce);
      });

      offline2.save('itemsS', itemsTemp);
      this.setState({
        dataSource: this.state.dataSource.cloneWithRows(itemsTemp),
      });
    });
  }

【问题讨论】:

    标签: javascript reactjs react-native ecmascript-6 setstate


    【解决方案1】:

    问题是他们提到的,但没有提供解决方案。 经过一段时间和一些外部帮助,这解决了我的问题: 只需移除监听器即可。

      componentWillUnmount() {
        this.itemsRef.off(); // this line
        this.connectedRef.off('value', this.handleValue);
      }
    

    【讨论】:

    • 对不起,安德烈 - 我可能应该在我的回复中包含这个代码 sn-p!很高兴您找到了自己的解决方案。
    【解决方案2】:

    我不确定它是否会导致你出错,但这是一个错误,你需要绑定你的事件处理程序,最好是在你的构造函数中,因为你想取消绑定(删除事件监听器),你需要参考相同的功能。

    constructor(props) {
        super(props);
        this.state = {
          dataSource: new ListView.DataSource({
            rowHasChanged: (row1, row2) => row1 !== row2,
          }),
        };
    
        const user = firebase.auth().currentUser;
        if (user != null) {
          this.itemsRef = this.getRef().child(`Stores/${user.uid}/`);
        } else {
          Alert.alert('Error', 'error!');
        }
        this.connectedRef = firebase.database().ref('.info/connected');
        this.handleValue = this.handleValue.bind(this) // here added
      }
    

    希望它有效,也可以解决您的问题。

    【讨论】:

    • 我认为这是我在(诚然不确定)答案中错过的神奇精灵尘埃。函数绑定很可能是关闭事件侦听器不起作用的原因。
    【解决方案3】:

    您并没有关闭所有事件处理程序。

    您为函数listenForItems() 内的itemsRef 对象上的'value' 事件创建一个事件处理程序。该事件处理程序调用setState()。该特定事件处理程序没有被关闭,这意味着每个后续数据库事件都会调用回调函数,该函数会尝试在旧的(未安装的)组件上调用 setState()

    您应该关闭componentWillUnmount() 中的所有事件处理程序,因此:

    componentWillUnmount() {
      this.itemsRef.off();
      this.connectedRef.off('value', this.handleValue);
    }
    

    【讨论】: