react中button上繫結react事件觸發原生body上繫結的事件
阿新 • • 發佈:2019-02-03
情景再現
需求:在Web頁面中新增一個使用移動裝置掃描二維碼的功能,在點選按鈕時顯示二維碼(在點選按鈕二維碼消失),點選非二維碼區域時將其隱藏起來。
出現問題:點選按鈕二維碼顯示之後,在點選按鈕,二維碼一直顯示,不會消失
分析原因:React合成事件系統的委託機制,依賴事件的冒泡機制完成委派,在點選按鈕時,同時觸發了body上繫結的事件
出錯程式碼:
class QrCode extends Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); this.handleClickQr = this.handleClickQr.bind(this); this.state = { active: false, }; } componentDidMount() { debugger document.body.addEventListener('click', e => { this.setState({ active: false }); }); // 不要將合成事件與原生事件混用 document.querySelector('.code').addEventListener('click', e => { e.stopPropagation(); }) } componentWillUnmount() { document.body.removeEventListener('click'); document.querySelector('.code').removeEventListener('click'); } handleClick() { this.setState({ active: !this.state.active, }); } render() { return ( <div className="qr-wrapper"> <button className="qr" >二維碼</button> <div className="code" style={{display:this.state.active ? 'block' : 'none'}} {onClick={this.handleClickQr} > <img src={require('./a.jpg')} /> </div> </div> ); } }
修改:不要將合成事件與原生事件混用,都改為原生事件
class QrCode extends Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); this.state = { active: false, }; } componentDidMount() { document.body.addEventListener('click', e => { this.setState({ active: false }); }); document.querySelector('.qr').addEventListener('click', e => { this.setState({ active: !this.state.active, }); // 必須加上阻止事件冒泡 e.stopPropagation(); }) // 不要將合成事件與原生事件混用 document.querySelector('.code').addEventListener('click', e => { e.stopPropagation(); }) } componentWillUnmount() { document.body.removeEventListener('click'); document.querySelector('.code').removeEventListener('click'); document.querySelector('.qr').removeEventListener('click'); } render() { return ( <div className="qr-wrapper"> <button className="qr" >二維碼</button> <div className="code" style={{display:this.state.active ? 'block' : 'none'}} > <img src={require('./a.jpg')} /> </div> </div> ); } }