我们正在构建一个自定义的React自动完成组件。
问题:当用户在文本字段中键入内容时,将显示匹配的建议,如果用户使用键盘的向下箭头进行选择,则一旦选择了向下箭头,下拉菜单就会关闭。
下面是代码,我无法提供此问题的实时示例
import React, { Component } from "react";
import {
string,
node,
bool,
element,
func,
arrayOf,
shape,
number
} from "prop-types";
import { MenuAnchor } from "@abc/react-menu";
import { TextField, TextFieldIcon } from "@abc/react-text-field";
import { DropDownMenu } from "./DropDownMenu";
export class Autocomplete extends Component {
static defaultProps = {
items: [],
value: "",
minChars: 3,
noOptionsText: <span>{"No options"}</span>,
textFieldClassName: "",
focusTextFieldAfterSelection: false
};
state = {
open: false,
value: this.props.value || "",
selected: this.props.value ? this.props.value : null,
filtered: [],
valueMap: this.props.items.reduce(reduceToMap, {})
};
focus = () => this.inputRef && this.inputRef.focus();
isSameObjectShallowComparison = (obj1, obj2) =>
Object.keys(obj1).length === Object.keys(obj2).length &&
Object.keys(obj1).every(key => obj1[key] === obj2[key]);
componentDidUpdate(prevProps, prevState) {
const newValueMap = this.props.items.reduce(reduceToMap, {});
// If the items are updated then update the valueMap in the state
if (!this.isSameObjectShallowComparison(newValueMap, prevState.valueMap)) {
this.setState({
valueMap: newValueMap
});
const newSelectedValue = newValueMap[this.state.selected];
// Update the value with the new selected value in the value map.
if (this.state.value !== newSelectedValue && newSelectedValue)
this.setState({
value: newSelectedValue,
open: false
});
}
}
onSelected = selected => {
this.setState({
selected,
open: false,
value: this.state.valueMap[selected]
});
this.props.onSelected && this.props.onSelected(selected);
this.props.focusTextFieldAfterSelection && this.focus();
};
componentDidMount() {
if (this.inputRef) {
this.inputRef.addEventListener("keydown", this.onKeyPress);
this.inputRef.addEventListener("focus", this.onFocus);
this.inputRef.addEventListener("blur", this.onBlur);
}
this.filterItems(this.props.value);
}
componentWillUnmount() {
if (this.inputRef) {
this.inputRef.removeEventListener("keydown", this.onKeyPress);
this.inputRef.removeEventListener("focus", this.onFocus);
this.inputRef.removeEventListener("blur", this.onBlur);
}
clearTimeout(this.timeOut);
}
onKeyPress = e => {
if (e.keyCode === 40 && this.menuRef && this.menuRef.childNodes) {
e.preventDefault();
e.stopPropagation();
const firstChild = this.menuRef.childNodes[0];
if (firstChild) {
firstChild.focus();
}
}
};
onFocus = e => this.setState({ open: this.state.filtered.length > 0 });
onBlur = e => {
this.timeOut = setTimeout(() => {
this.setState({ open: false });
clearTimeout(this.timeOut);
}, 175);
};
onChange = e => {
const value = e.target.value;
this.filterItems(value);
this.setState({ open: value.length >= this.props.minChars });
let newSelectedValue = this.state.selected;
if (value.indexOf(newSelectedValue) === -1) {
this.setState({ selected: null });
newSelectedValue = null;
}
this.props.onChange && this.props.onChange(e, value, newSelectedValue);
};
filterItems(value) {
const filtered = [];
if (value && value.length >= this.props.minChars) {
const { items } = this.props;
const re = new RegExp(value, "i");
for (let i = 0; i < items.length; i++) {
if (items[i].label.search(re) > -1) {
filtered.push({ ...items[i] });
}
}
this.setState({ filtered, value });
} else {
this.setState({ value, filtered, open: false });
}
}
clearSelection = () => {
this.setState({ open: false, selected: null, value: "", filtered: [] });
if (this.props.onSelected) {
this.props.onSelected(null);
}
};
render() {
const {
type,
pattern,
noOptionsText,
leadingIcon,
trailingIcon,
inputRef,
onChange,
minChars,
items,
onSelected,
value,
textFieldClassName,
focusTextFieldAfterSelection,
...other
} = this.props;
const { open, filtered } = this.state;
return (
<MenuAnchor>
<TextField
value={this.state.value}
onChange={this.onChange}
inputRef={ref => {
this.inputRef = ref;
}}
className={`autocomplete--input ${textFieldClassName}`}
trailingIcon={
this.state.value.length > 0 ? (
<TextFieldIcon
onClick={this.clearSelection}
className="-autocomplete--icon"
>
cancel
</TextFieldIcon>
) : (
<TextFieldIcon className="-autocomplete--icon" />
)
}
{...other}
/>
<DropDownMenu
innerRef={ref => (this.menuRef = ref)}
noOptionsText={noOptionsText}
onSelected={this.onSelected}
items={filtered}
open={open}
value={this.state.value}
/>
</MenuAnchor>
);
}
}
function reduceToMap(prev, curr) {
prev[curr.value] = curr.label;
return prev;
}
如果您可以从上面的代码中找出一些内容,以防止单击帮助面板上的向下箭头时下拉菜单关闭。