React 中太多的重新渲染

Too many re-renders in React

该程序应接受用户输入的内容,搜索数据,然后 return 结果会出现在下拉列表中。 当用户输入超过 3 个符号时,将调用 Search() 并且我得到“错误:重新呈现太多”。找不到渲染循环在哪里。

import LTCityNames from "../lt-city-names.json"; //JSON object

const Openweathermap = () => {
     const [searchList, setSearcList] = useState([]); //drop down list according to search word
     const [text, setText] = useState(""); //text in the input field
  
     const Search = (userinput) => {
         let correctResult = "";
         let dropdownList = [];

     const regex = new RegExp(`^${userinput}`, "i");
        for (let i = 0; i < LTCityNames.length; i++) {
           correctResult = regex.test(LTCityNames[i].name);
        if (correctResult){
           dropdownList.push(LTCityNames[i]);
           setSearcList(dropdownList);
        }   
      }
  };

     const onChangeInput = (userinput) => {
       setText(userinput);
       if (userinput.length > 2) {
         Search(userinput);
       }
     };

   return (
     <input
      value={text}
      onChange={(e) => {onChangeInput(e.target.value)} }
      type="text"
      placeholder="Enter address"
     ></input>
     <div id="myDropdownWeather" className="dropdown-content">
       {searchList.map((itemInArray) => {
         return (
           <ul>
             <li>{itemInArray.name}</li>
           </ul>
         );
       })
}
import LTCityNames from "../lt-city-names.json"; //JSON object

const Openweathermap = () => {
     const [searchList, setSearcList] = useState([]); //drop down list according to search word
     const [text, setText] = useState(""); //text in the input field
  
     const Search = (userinput) => {
         let correctResult = "";
         let dropdownList = [];

     const regex = new RegExp(`^${userinput}`, "i");
        for (let i = 0; i < LTCityNames.length; i++) {
           correctResult = regex.test(LTCityNames[i].name);
        if (correctResult){
           dropdownList.push(LTCityNames[i]);
           setSearcList(dropdownList);
        }   
      }
  };

     const onChangeInput = (userinput) => {
       setText(userinput);
       if (userinput.length > 2) {
         Search(userinput);
       }
     };


//remove value={text}
       return (
         <input
          onChange={(e) => {onChangeInput(e.target.value)} }
          type="text"
          placeholder="Enter address"
         ></input>
         <div id="myDropdownWeather" className="dropdown-content">
           {searchList.map((itemInArray) => {
             return (
               <ul>
                 <li>{itemInArray.name}</li>
               </ul>
             );
           })
    }

删除值 = {文本}

我认为你必须像这样使用 useEffect:

  const [text, setText] = useState(""); //text in the input field

  const lastFilter = useRef(text);

  useEffect(() => {
    if (lastFilter.current !== text && text.lenght>2) {
        Search(userinput);
        lastFilter.current = text;
    }
}, [text]);

 const onChangeInput = (event) => {
   var userinput=event.target.value;
   setText(userinput);
 };

并改变

onChange={(e) => {onChangeInput(e.target.value)} }

onChange={(e) => {onChangeInput(e)} } 

首先: 为什么会出现“错误:太多 re-renders”?

当您使用 React 功能组件时,每次调用 "setState" React 都会重新加载您的所有组件,并且由于您在组件内部使用函数,因此这些函数也是每次组件更改时都会加载。因此,当您键入搜索时,该元素将 re-render 不受控制。

解决问题:

  • 每次你想在 React 函数式组件中使用一个函数时,你必须使用 React.useCallback 因为这样你可以准确控制函数何时应该被重新加载内存防止你得到的错误。
  • 还有一点,在你的 return 里面,当你使用 react 时,你不能 return 多个 JSX 元素,这也会给你带来很多问题,解决这个问题你可以使用 片段元素 <> ... 或任何其他将包含所有其他元素的主元素 (片段元素不会干扰你 CSS).

代码:

import React, { useCallback, useState } from 'react';
import LTCityNames from '../lt-city-names.json'; // JSON object

const Openweathermap = () => {
  const [searchList, setSearcList] = useState([]); // drop down list according to search word
  const [text, setText] = useState(''); // text in the input field

  const Search = useCallback((userinput) => {
    const correctResult = '';
    const dropdownList = [];

    const regex = new RegExp(`^${userinput}`, 'i');
    for (let i = 0; i < LTCityNames.length; i++) {
      const correctResult = regex.test(LTCityNames[i].name);
      if (correctResult) {
        dropdownList.push(LTCityNames[i]);
        setSearcList(dropdownList);
      }
    }
  }, []);

  const onChangeInput = useCallback(
    (e) => {
      const userinput = e.target.value;
      setText(userinput);
      if (userinput.length > 2) {
        Search(userinput);
      }
    },
    [Search],
  );

  return (
    <> // Fragment element start
      <input
        value={text}
        onChange={(e) => onChangeInput(e)}
        type="text"
        placeholder="Enter address"
      />
      <div id="myDropdownWeather" className="dropdown-content">
        {searchList.map((itemInArray) => {
          return (
            <ul>
              <li>{itemInArray.name}</li>
            </ul>
          );
        })}
      </div>
    </> // Fragment element end
  );
};

了解useCallback:

  • useCallback 是一个 React 函数,它将接收 2 个参数,第一个是你的函数,第二个是参数数组,当更改时将触发函数在内存中重新加载(每次你使用一个元素来自函数本身之外,您需要将其用作参数以将函数重新加载到内存中。

const myReactFunction = useCallback(() => {}, [a,b,c....] )

改进你的组件Return:

  • 您不需要使用下面列出的任何提示,但它们会提高代码的可读性。

  • 由于您使用 (e) => onChangeInput(e) 调用您的输入 onChange 您可以将您的输入更改为仅 onChangeInput:

     <input
         value={text}
         onChange={onChangeInput} // same as (e) => function(e) 
         type="text"
         placeholder="Enter address"
     />
    
  • 第二个提示在你的映射函数中,因为你使用的是箭头函数,所以你不需要输入 return():

     {searchList.map((itemInArray) => (
         <ul>
             <li>{itemInArray.name}</li>
         </ul>
     ))}