我在窗口中添加了一个侦听器来检测onClickOutside
类型的场景(具体来说,在菜单外单击时折叠菜单)。这是包含相关代码的组件:
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import LinkedStateMixin from 'react-addons-linked-state-mixin';
import styles from './DescriptiveSelect.css';
import classNames from 'classnames';
import _ from 'underscore';
export default class DescriptiveSelect extends Component {
static propTypes = {
attrForOptionLabel: PropTypes.string,
attrForOptionValue: PropTypes.string,
children: PropTypes.string,
className: PropTypes.string,
disabled: PropTypes.bool,
nullLabel: PropTypes.string,
onUpdate: PropTypes.func,
options: PropTypes.array,
selectedLabel: PropTypes.string,
selectedOption: PropTypes.number,
valueLink: PropTypes.object
}
mixins: [LinkedStateMixin]
static defaultProps = {
attrForOptionLabel: 'name',
attrForOptionValue: 'id',
disabled: false,
selectedOption: 0,
selectedLabel: 'Select one…'
}
state = {
isOpen: false,
options: []
}
componentDidMount() {
this.buildOptions(this.props.options);
window.addEventListener('click', this.onDocumentClick.bind(this));
}
componentWillUnmount() {
window.removeEventListener('click', this.onDocumentClick);
}
onDocumentClick(event) {
const theLabel = ReactDOM.findDOMNode(this);
if (theLabel && theLabel.contains(event.target)) {
this.setState({isOpen: !this.state.isOpen});
} else {
this.setState({isOpen: false});
}
event.preventDefault();
}
handleSelection(option) {
if (this.props.onUpdate) {
this.props.onUpdate(option.id);
}
this.setState({'isOpen': false});
}
/**
* Build out <select> menu options
* Data must be formatted with the following attributes:
* const theData = [
* {
* id: 1,
* sequence: 0,
* short_name: 'App'
* }
* ];
* @param {array} data The data to convert, either from an endpoint
* response or passed in via the `options` prop.
*/
buildOptions(data) {
const _this = this;
const results = data;
const resultLength = results.length;
const {
attrForOptionValue,
attrForOptionLabel
} = this.props;
// Sort data by sequence attribute
_.sortBy(results, 'sequence');
// Cycle through JSON results and create <option> elements
for (let i = 0; i < resultLength; i++) {
const option = results[i];
_this.state.options.push(
<option key={option.id} value={option[attrForOptionValue]}>{option[attrForOptionLabel]}</option>
);
_this.forceUpdate();
}
}
render() {
const {
className,
nullLabel,
options
} = this.props;
// Classes for select menu
const selectClasses = classNames({
[styles.LabelWrapper]: true,
[className]: className
});
/**
* Contents of the custom select.
* Taken from the `options` prop that should be passed in.
*/
const optionNodes = options.map((option) => {
return (
<li className={styles.Option} key={option.id} onClick={this.handleSelection.bind(this, option)}>
<div className={styles.OptionLabel}>a <strong>{option.name}</strong></div>
<div className={styles.OptionDetail}>{option.description}</div>
</li>
);
});
return (
<label className={selectClasses} ref="theLabel">
<select
className={styles.Select}
nullLabel={nullLabel}
options={options}
ref="theSelect"
valueLink={this.props.valueLink}
>
{ nullLabel ? <option value="">{nullLabel}</option> : null }
{ this.state.options }
</select>
{ this.state.isOpen ? <div className={styles.Menu}>
<ul className={styles.OptionsWrapper}>
{optionNodes}
</ul>
<footer className={styles.Footer}>
<p><a href="#">Learn more</a> about App.typography titles.</p>
</footer>
</div> : null }
</label>
);
}
}
我不知道为什么,但它实际上并没有删除监听器,所以我最终在控制台中找到了其中的几个:
未捕获错误:不变违规:调用了findDOMNode 未安装的组件。
此代码中有任何可能导致此问题的内容?
答案 0 :(得分:4)
您正在尝试删除未绑定的函数。 .bind
会返回新函数,即this.onDocumentClick.bind(this) !== this.onDocumentClick
。
您应该在构造函数中绑定方法一次,然后在整个过程中使用该方法:
constructor(props) {
super(props);
this.onDocumentClick = this.onDocumentClick.bind(this);
// use this.onDocumentClick everywhere
}