提问者:小点点

在未安装的组件上调用setState


在我的许多组件中,我需要执行以下操作:

handleSubmit() {
  this.setState({loading: true})
  someAsyncFunc()
    .then(() => {
      return this.props.onSuccess()
    })
    .finally(() => this.setState({loading: false}))
}

函数代码

  • 可能是promise,也可能不是promise(如果是promise,加载应保持真实,直到问题得到解决)
  • 可能卸载组件,也可能不卸载组件(它可能关闭此组件所在的模式,甚至导航到其他页面)

如果函数卸载组件,。setState({loading:false})显然会触发一个警告无法对未安装的组件调用setState(或forceUpdate)

我的两个问题:

  1. 有没有一个简单的方法来避免这个问题?我不想在componentDidMountcomponentWillUnmount中设置一些\u isMounted变量,然后在需要时在我的大多数组件中检查它,而且下次写类似这样的东西时,我可能会忘记这样做

编辑:第二个问题对我来说比第一个问题更重要一点。如果这真的是一个问题,我只是不能在未安装的组件上调用setState,我可能会自己找到一些解决方案。但是我很好奇我是否不能忽略它。

问题的实例:

const someAsyncFunc = () => new Promise(resolve => {
  setTimeout(() => {
  	console.log("someAsyncFunc resolving");
    resolve("done");
  }, 2000);
});

class Example extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {loading: false};
  }
  
  componentDidMount() {
    setTimeout(() => this.handleSubmit(), 100);
  }
  
  handleSubmit() {
    this.setState({loading: true})
    someAsyncFunc()
      /*
      .then(() => {
        return this.props.onSuccess()
      })
      */
      .finally(() => this.setState({loading: false}))
  }
  
  render() {
    return <div>{String(this.state.loading)}</div>;
  }
}

class Wrapper extends React.Component {
	constructor(props, ...rest) {
  	super(props, ...rest);
    this.state = {
    	children: props.children
    };
  }
	componentDidMount() {
  	setTimeout(() => {
    	console.log("removing");
    	this.setState({children: []});
    }, 1500)
  }
	render() {
  	return <div>{this.state.children}</div>;
  }
}

ReactDOM.render(
	<Wrapper>
    <Example />
	</Wrapper>,
  document.getElementById("root")
);
.as-console-wrapper {
  max-height: 100% !important;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>

共3个答案

匿名用户

不幸的是,你必须自己跟踪“isMounted”。为了简化控制流程,可以使用async/await

handleSubmit() {
  this.setState({loading: true})
  try {
    await someAsyncFunction()
    await this.props.onSuccess()
  } finally {
    if (this._isMounted) {
      this.setState({loading: false})
    }
  }
}

react文档中实际提到了这一点,它指向此解决方案:https://gist.github.com/bvaughn/982ab689a41097237f6e9860db7ca8d6

如果您的某些AsyncFunction支持取消,那么您应该像本文所鼓励的那样在组件中执行此操作。但是,然后-当然-检查返回值,最终不调用this.props.on成功

匿名用户

class myClass extends Component {
  _isMounted = false;

  constructor(props) {
    super(props);

    this.state = {
      data: [],
    };
  }

  componentDidMount() {
    this._isMounted = true;
    this._getData();
  }

  componentWillUnmount() {
    this._isMounted = false;
  }

  _getData() {
    axios.get('example.com').then(data => {
      if (this._isMounted) {
        this.setState({ data })
      }
    });
  }


  render() {
    ...
  }
}

匿名用户

您应该能够使用这个_i安装,检查组件是否已实际安装。

handleSubmit() {
  this.setState({loading: true})
  someAsyncFunc()
    .then(() => {
      return this.props.onSuccess()
    })
    .finally(() => {
      if (this && this._isMounted) { // check if component is still mounted
        this.setState({loading: false})
      }
    })
}

但是请注意,这种方法被认为是一种模式。https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html