问题提出:
今天在使用React的时候发生了死循环情况,现在拿出来跟大家讨论讨论,其中大致代码如下:
import React from 'react';
import ReactDOM from 'react-dom';
const Button = ({ onClick, children }) => {
return (
<button type="button"
onClick={event => {
if (!!onClick) {
onClick(event);
}
}}>{children}</button>
);
}
function handleClick() {
this.setState({count:++this.state.count});
console.log("click done!");
}
class App extends React.Component {
constructor(props) {
super(props);
this.state={count :0,color:"red"}
}
componentDidMount() {
}
render() {
return (
<div>
<Button onClick={handleClick.call(this)}>点我</Button>
{this.state.count}
</div>
)
}
}
export default App;
ReactDOM.render(<App />, document.querySelector("#app"));
运行后浏览器会报这样的错误:
Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
在低版本的React可能不会出现上面这样的错误,而直接进入死循环中,直到内存消耗殆尽。
问题分析:
代码的效果很简单就是点击一下按钮,数字加一下。这里把按钮封装了一下。请看render
方法里面的onClick
方法,这里调用handleClick.call(this)
而这个并不是一个函数,而是函数的执行,所以到这一行React会执行这个函数,但是函数里面又调用了setState
方法,这个方法改变状态后又会重新渲染DOM,也就会再次调用render
方法。同样的,该方法执行的时候又会调用handleClick.call(this)
,再次循环上面的逻辑,因此陷入了死循环之中。
解决问题:
这里handleClick.call(this)
有问题,我们调用这的目的是为了绑定this
,而不需要执行,所以这行代码改成handleClick.bind(this)
即可。当然还有另外一种解决办法,就是在方法中调用函数,代码如下:
<Button onClick={()=>{
handleClick.call(this)
}}>点我</Button>
扩展延伸:
这里出现错误的原因是有call
方法调用所导致的,那么我们很有必要了解一下call
、apply
、bind
三个方法的区别,这三者都是用来改变this
的指向的,但是也有不一样的地方,具体区别如下:
1.
call
和apply
的效果是一样的都是绑定this
,并且调用
。只是语法有所区别。call绑定this的语法是这样的func.call(this, arg1, arg2)
,apply绑定this的语法是这样的func.apply(this, [arg1, arg2])
。也就是说除了第一个参数是绑定对象外,call
的参数和原本的函数调用顺序是一致的,但是apply
的第二个参数是原先参数的一个数组形式。
2.bind
是ES5(几乎现在所有的主流浏览器都支持)增加语法,它只是绑定this
,但不会立即调用。