Sim*_*ong 0 javascript reactjs react-hooks
因此,我正在尝试编写一段简单的(据说是!)代码,在用户键入时过滤数组中的字符串。
这是我所做的缩减版本。
简要解释一下,我有两个相互交互的组件。
FilterExample 其中包含要过滤的字符串列表MySearchBar它通过 prop 传递要过滤的字符串列表elementsToFilter。当搜索文本发生变化时,MySearchBar它会计算哪些索引elementsToFilter仍应显示并通过FilterExample回调将它们传递回onFilterTextChanged
我无法解释为什么在无限循环中出现以下错误,以及useEffect的依赖数组之一(即currentFilterIndices和fruits)将如何在每次渲染时发生变化
Warning: Maximum update depth exceeded. This can happen when a component calls
setState inside useEffect, but useEffect either doesn't have a dependency array,
or one of the dependencies changes on every render.
in FilterExample (at src/index.js:9)
in StrictMode (at src/index.js:8)
Run Code Online (Sandbox Code Playgroud)
import React, { useState, useEffect } from "react";
import MySearchBar from "./MySearchBar";
/** Basic example of a list of strings filterable using PilzDashboardActionBar*/
function FilterExample() {
const fruits = [
"Apples ",
"Oranges ",
"Bananas ",
"Pears ",
"Peaches ",
"Grapes "
];
const [currentFilterIndices, setCurrentFilterIndices] = useState([
...Array(fruits ? fruits.length : 0).keys()
]);
const [stringsToDisplay, setStringsToDisplay] = useState(fruits);
//When the filter indices are sent in a callback from MySearchBar I
//filter the list of strings.f
useEffect(() => {
setStringsToDisplay(
fruits.filter((currString, currIndex) => {
return currentFilterIndices.includes(currIndex);
})
);
}, [currentFilterIndices, fruits]);
//This calls back from the MySearchBar when the user types filtered text into it.
const handleActionBarFilterTextChanged = filteredElementIndicesFromSearchBar => {
setCurrentFilterIndices(filteredElementIndicesFromSearchBar);
};
return (
<>
<MySearchBar
elementsToFilter={stringsToDisplay}
onFilterTextChanged={handleActionBarFilterTextChanged}
/>
{stringsToDisplay}
</>
);
}
export default FilterExample;
Run Code Online (Sandbox Code Playgroud)
import React from "react";
import { TextField } from "@material-ui/core";
function MySearchBar(props) {
//Destructure props
const { elementsToFilter, onFilterTextChanged } = props;
const handleSearchTextChange = currSearchText => {
let filterIndices = [];
elementsToFilter.forEach((currItem, currIndex) => {
if (currItem.toUpperCase().includes(currSearchText.toUpperCase())) {
filterIndices.push(currIndex);
}
});
onFilterTextChanged(filterIndices);
};
return (
<div>
<TextField
onChange={event => {
handleSearchTextChange(event.target.value);
}}
/>
</div>
);
}
export default MySearchBar;
Run Code Online (Sandbox Code Playgroud)
因为您在每次渲染时都创建了一个新的水果数组。
由于 useEffect 对fruits 数组进行了浅层比较,因此如果数组发生变化,它将触发重新渲染。
由于每次渲染都是新的,因此会导致无限循环。只需将数组生成从函数组件中移出或用 useMemo 包装它。
import React, { useState, useEffect } from "react";
import MySearchBar from "./MySearchBar";
const fruits = [
"Apples ",
"Oranges ",
"Bananas ",
"Pears ",
"Peaches ",
"Grapes "
];
/** Basic example of a list of strings filterable using PilzDashboardActionBar*/
function FilterExample() {
const [currentFilterIndices, setCurrentFilterIndices] = useState([
...Array(fruits ? fruits.length : 0).keys()
]);
const [stringsToDisplay, setStringsToDisplay] = useState(fruits);
//When the filter indices are sent in a callback from MySearchBar I
//filter the list of strings.f
useEffect(() => {
setStringsToDisplay(
fruits.filter((currString, currIndex) => {
return currentFilterIndices.includes(currIndex);
})
);
}, [currentFilterIndices, fruits]);
//This calls back from the MySearchBar when the user types filtered text into it.
const handleActionBarFilterTextChanged = filteredElementIndicesFromSearchBar => {
setCurrentFilterIndices(filteredElementIndicesFromSearchBar);
};
return (
<>
<MySearchBar
elementsToFilter={stringsToDisplay}
onFilterTextChanged={handleActionBarFilterTextChanged}
/>
{stringsToDisplay}
</>
);
}
export default FilterExample;
Run Code Online (Sandbox Code Playgroud)