我正在制作一个可搜索的下拉菜单并收到以下 eslint 警告:
<块引用>React Hook useEffect 缺少依赖项:“filterDropDown”和“restoreDropDown”。包括它们或删除依赖项数组。
import React, { useState, useEffect, useCallback } from "react";
const SearchableDropDown2 = () => {
const [searchText, setSearchText] = useState("");
const [dropdownOptions, setDropdownOptions] = useState([
"React",
"Angular",
"Vue",
"jQuery",
"Nextjs",
]);
const [copyOfdropdownOptions, setCopyOfDropdownOptions] = useState([
...dropdownOptions,
]);
const [isExpand, setIsExpand] = useState(false);
useEffect(() => {
searchText.length > 0 ? filterDropDown() : restoreDropDown();
}, [searchText]);
const onClickHandler = (e) => {
setSearchText(e.target.dataset.myoptions);
setIsExpand(false);
};
const onSearchDropDown = () => setIsExpand(true);
const closeDropDownHandler = () => setIsExpand(false);
const filterDropDown = useCallback(() => {
const filteredDropdown = dropdownOptions.filter((_) =>
_.toLowerCase().includes(searchText.toLowerCase())
);
setDropdownOptions([...filteredDropdown]);
}, [dropdownOptions]);
const restoreDropDown = () => {
if (dropdownOptions.length !== copyOfdropdownOptions.length) {
setDropdownOptions([...copyOfdropdownOptions]);
}
};
const onSearchHandler = (e) => setSearchText(e.target.value.trim());
return (
<div style={styles.mainContainer}>
<input
type="search"
value={searchText}
onClick={onSearchDropDown}
onChange={onSearchHandler}
style={styles.search}
placeholder="search"
/>
<button disabled={!isExpand} onClick={closeDropDownHandler}>
-
</button>
<div
style={
isExpand
? styles.dropdownContainer
: {
...styles.dropdownContainer,
height: "0vh",
}
}
>
{dropdownOptions.map((_, idx) => (
<span
onClick={onClickHandler}
style={styles.dropdownOptions}
data-myoptions={_}
value={_}
>
{_}
</span>
))}
</div>
</div>
);
};
const styles = {
mainContainer: {
padding: "1vh 1vw",
width: "28vw",
margin: "auto auto",
},
dropdownContainer: {
width: "25vw",
background: "grey",
height: "10vh",
overflow: "scroll",
},
dropdownOptions: {
display: "block",
height: "2vh",
color: "white",
padding: "0.2vh 0.5vw",
cursor: "pointer",
},
search: {
width: "25vw",
},
};
我尝试将 filterDropDown
包裹在 useCallback
中,但随后可搜索下拉菜单停止工作。
以下是包含 useCallback
的更改:
const filterDropDown = useCallback(() => {
const filteredDropdown = dropdownOptions.filter((_) =>
_.toLowerCase().includes(searchText.toLowerCase())
);
setDropdownOptions([...filteredDropdown]);
}, [dropdownOptions, searchText]);
答案 0 :(得分:2)
我的建议是,在您实现目标的情况下,根本不要使用 useEffect
。
import React, {useState} from 'react';
const SearchableDropDown = () => {
const [searchText, setSearchText] = useState('');
const dropdownOptions = ['React','Angular','Vue','jQuery','Nextjs'];
const [isExpand, setIsExpand] = useState(false);
const onClickHandler = e => {
setSearchText(e.target.dataset.myoptions);
setIsExpand(false);
}
const onSearchDropDown = () => setIsExpand(true);
const closeDropDownHandler = () => setIsExpand(false);
const onSearchHandler = e => setSearchText(e.target.value.trim());
return (
<div style={styles.mainContainer}>
<input
type="search"
value={searchText}
onClick={onSearchDropDown}
onChange={onSearchHandler}
style={styles.search}
placeholder="search"
/>
<button disabled={!isExpand} onClick={closeDropDownHandler}>
-
</button>
<div
style={
isExpand
? styles.dropdownContainer
: {
...styles.dropdownContainer,
height: '0vh',
}
}
>
{dropdownOptions
.filter(opt => opt.toLowerCase().includes(searchText.toLowerCase()))
.map((option, idx) =>
<span key={idx} onClick={onClickHandler} style={styles.dropdownOptions} data-myoptions={option} value={option}>
{option}
</span>
)}
</div>
</div>
);
};
const styles = {
mainContainer: {
padding: '1vh 1vw',
width: '28vw',
margin: 'auto auto'
},
dropdownContainer: {
width: '25vw',
background: 'grey',
height: '10vh',
overflow: 'scroll'
},
dropdownOptions: {
display: 'block',
height: '2vh',
color: 'white',
padding: '0.2vh 0.5vw',
cursor: 'pointer',
},
search: {
width: '25vw',
},
};
只需过滤 dropDownOptions
,然后再将它们映射到 span
元素。
此外,如果 dropDownOptions
的列表会改变,那么使用 setState
否则只需将其保留为一个简单的列表。如果 dropDownOptions
来自 api 调用,则在 setState
挂钩中使用 useEffect
。
答案 1 :(得分:0)
您当前的代码:
// 1. Filter
const filterDropDown = useCallback(() => {
const filteredDropdown = dropdownOptions.filter((_) =>
_.toLowerCase().includes(searchText.toLowerCase())
);
setDropdownOptions([...filteredDropdown]);
}, [dropdownOptions]);
// 2. Restore
const restoreDropDown = () => {
if (dropdownOptions.length !== copyOfdropdownOptions.length) {
setDropdownOptions([...copyOfdropdownOptions]);
}
};
// 3. searchText change effect
useEffect(() => {
searchText.length > 0 ? filterDropDown() : restoreDropDown();
}, [searchText]);
以上代码产生 eslint 警告:
<块引用>React Hook useEffect 缺少依赖项:“filterDropDown”和“restoreDropDown”。包括它们或删除依赖数组
但是按照警告的内容做了之后,我们的代码最终看起来是这样的:
// 1. Filter
const filterDropDown = useCallback(() => {
const filteredDropdown = dropdownOptions.filter((_) =>
_.toLowerCase().includes(searchText.toLowerCase())
);
setDropdownOptions(filteredDropdown);
}, [dropdownOptions, searchText]);
// 2. Restore
const restoreDropDown = useCallback(() => {
if (dropdownOptions.length !== copyOfdropdownOptions.length) {
setDropdownOptions([...copyOfdropdownOptions]);
}
}, [copyOfdropdownOptions, dropdownOptions.length]);
// 3. searchText change effect
useEffect(() => {
searchText.length > 0 ? filterDropDown() : restoreDropDown();
}, [filterDropDown, restoreDropDown, searchText]);
但是上面的代码已经形成了一个无限循环,因为:
searchText
或 dropdownOptions
更改时重新创建。这是有道理的。)copyOfdropdownOptions
或 dropdownOptions.length
更改时重新创建。这也是有道理的。)searchText
变化效果看起来糟糕。此效果将在以下情况下运行:
searchText
已更改 (OK),或我们可以清楚地看到第 3 点(a、b、c)中的无限循环。
可能有几种方法。一种可能是:
下面的copy是常量,所以要么把它保存在一个Ref中,要么把它移到组件定义之外:
const copyOfdropdownOptions = useRef([...dropdownOptions]);
并且,移动钩子内部的“过滤器”和“恢复”函数(即不需要在外部定义它并将其作为依赖项),如下所示:
useEffect(() => {
if (searchText.length) {
// 1. Filter
setDropdownOptions((prev) =>
prev.filter((_) => _.toLowerCase().includes(searchText.toLowerCase()))
);
} else {
// 2. Restore
setDropdownOptions([...copyOfdropdownOptions.current]);
}
}, [searchText]);
如您所见,上述效果仅在更改 searchText
时才会运行。