在setState之后调用addEventListener似乎运行事件两次

时间:2017-10-11 05:28:11

标签: reactjs

如果我在调用setState之后添加一个事件监听器,我希望只有在我再次触发该事件时才会调用该事件监听器。但是,从下面的示例中,当我单击div时,在状态更改后调用事件侦听器。

如果我删除了addEventListener或者我在evt.stopPropagation()内调用了toggleOpen,就不会发生这种情况,但我想知道为什么事件监听器会被调用我改变状态后设置它。

不会setState异步更改状态,这意味着在事件传播后会调用回调吗?

import React from 'react';
import classNames from 'classnames';

import css from './Dropdown.scss';


export class Dropdown extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      open: false,
    }
  }

  toggleOpen = (evt) => {
    window.removeEventListener('click', this.toggleOpen);

    this.setState({
      open: !this.state.open,
    }, () => {
      window.addEventListener('click', this.toggleOpen);
    });
  }

  render() {
    const dropdownContentClasses = classNames(css.dropdownContent, {
      [css.dropdownContent_open]: this.state.open,
    });

    console.log(this.state.open)

    return (
      <div className={css.dropdownContainer}>
        <div onClick={this.toggleOpen}>
          {this.props.title}
        </div>
        <div className={dropdownContentClasses}>
          {this.props.children}
        </div>
      </div>
    )
  }
}

4 个答案:

答案 0 :(得分:1)

2件事。首先,从https://reactjs.org/docs/react-component.html#setstate开始,您希望使用prevState属性来计算新状态:

toggleOpen = evt => {
  this.setState((prevState, props) => {
    return { open : ! prevState.open };
  });
}

其次,最好在componentDidMount()阶段添加事件处理程序,并在componentWillUnmount()阶段删除它:

componentDidMount() {
  window.addEventListener('click', this.toggleOpen);
}
componentWillUnmount() {
  window.removeEventListener('click', this.toggleOpen);
}

答案 1 :(得分:1)

似乎传播到达窗口时,会添加侦听器,然后调用eventListener。添加event.stopPropagation应该解决它。

答案 2 :(得分:1)

这太奇怪了!

document上添加事件监听器而不是window可以解决问题。

https://jsfiddle.net/y09h3nuf/

答案 3 :(得分:0)

我建议您使用componentDidMount检查在state.open内添加事件,而不是在toggleOpen上添加窗口事件。

componentDidMount() {
    window.addEventListener('click', (e) => {
        this.state.open && this.toggleOpen(e);
    });
}

我在这里添加了一个小提琴:https://jsfiddle.net/69z2wepo/87489/