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.
为什么在卸载的组件中仍会出现关于设置状态的错误?在错误跟踪中,它指向setTimeout
中的foo()
。我清除了异步计时器,并在执行api调用之前添加了检查-我看不到此警告的来源。
componentDidMount() {
this.setState({alive: true});
this.foo();
}
componentWillUnmount() {
this.setState({alive: false, timer: 0});
}
foo() {
if (!this.state.alive) return;
fetch('/api/etc/', {method: 'GET', headers: {'Cache-Control': 'no-cache'}})
.then(res => res.json())
.then(json => {
if (!json.length) return;
this.setState((prevState) => ({
timer: setTimeout(this.foo.bind(this), 500)
});
});
}
答案 0 :(得分:6)
如下面的the comment中的Evan Trimboli所指出的,无需将超时ID以it doesn't impact rendering
的状态存储。
因此将timeout
ID存储在类实例中,并用它清除componentWillUnmount
中的超时。
运行以下代码以查看其运行情况
class TodoApp extends React.Component {
timeout = 0;
hello = () => console.log("hello world!")
componentDidMount() {
this.timeout = setTimeout(this.hello, 500);
}
componentWillUnmount() {
clearTimeout(this.timeout);
}
render() {
return (
<div>demo</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
React.useEffect让我们将设置和拆卸逻辑放在同一块中。
const TodoApp = () => {
const hello = () => console.log("hello world!")
React.useEffect(() => {
const timeout = setTimeout(hello, 500);
return () => clearTimeout(timeout);
});
return (
<div>demo</div>
)
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
<script src="https://unpkg.com/react@16.8.4/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.8.4/umd/react-dom.production.min.js"></script>
<div id="app"></div>
在状态中存储超时ID,并使用它清除componentWillUnmount
中的超时。
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.state = {
timeout: null
}
this.hello = this.hello.bind(this);
}
hello() { console.log("hello world!"); }
componentDidMount() {
const timeout = setTimeout(this.hello, 500);
this.setState({timeout});
}
componentWillUnmount() {
clearTimeout(this.state.timeout);
}
render() {
return (
<div>demo</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>