收到警告:过滤列表时超出了最大更新深度

Sim*_*ong 0 javascript reactjs react-hooks

因此,我正在尝试编写一段简单的(据说是!)代码,在用户键入时过滤数组中的字符串。

这是我所做的缩减版本。

简要解释一下,我有两个相互交互的组件。

  1. FilterExample 其中包含要过滤的字符串列表
  2. MySearchBar它通过 prop 传递要过滤的字符串列表elementsToFilter

当搜索文本发生变化时,MySearchBar它会计算哪些索引elementsToFilter仍应显示并通过FilterExample回调将它们传递回onFilterTextChanged

我无法解释为什么在无限循环中出现以下错误,以及useEffect的依赖数组之一(即currentFilterIndicesfruits)将如何在每次渲染时发生变化

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)

Dom*_*987 5

因为您在每次渲染时都创建了一个新的水果数组。

由于 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)