简体   繁体   English

ReactJS:useMemo 钩子解决了我的无限重新渲染问题而不是 useEffect

[英]ReactJS : useMemo hook fixed my infinite rerender issue instead of useEffect

Without useMemo: infinite rerendering component没有 useMemo:无限重新渲染组件

I had an issue with the code below compiling successfully but rerendering the component infinite times and I get this error in console: 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.我遇到了以下代码编译成功但重新渲染组件无限次的问题,我在控制台中收到此错误: 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. 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.

import { Box, Flex } from "@chakra-ui/react";
import { useEffect, useState } from "react";
import Section, { SectionHeading } from "../../components/UI/Section";

import FaqList from "../../data/FaqList";
import FAQFilterBtn from "./FAQFilterBtn";
import FAQItems from "./FAQItems";

const allFaqItems = Object.values(FaqList).map((elem) => elem.content);

const allFaq = allFaqItems.map((elem, index) => (
  <Box key={index}>
    <FAQItems items={elem} faqHeading={Object.values(FaqList)[index].heading} />
  </Box>
));

const FAQSection = () => {
  const [displayList, setDisplayList] = useState(allFaq);
  const [activeFilter, setActiveFilter] = useState("All");

  const filteredAllFaq = 
      allFaqItems.map((elem, index) => {
        const faq =
          activeFilter === Object.values(FaqList)[index].heading ||
          activeFilter === "All" ? (
            elem.length ? (
              <FAQItems
                items={elem}
                faqHeading={Object.values(FaqList)[index].heading}
              />
            ) : (
              ""
            )
          ) : (
            ""
          );
        return <Box key={index}>{faq}</Box>;
      });

  const changeFilter = (filter: string) => {
    setActiveFilter(filter);
  };

  useEffect(() => {
    setDisplayList(filteredAllFaq);
  }, [activeFilter, filteredAllFaq]);

 
  console.log(activeFilter);

  return (
    <Section id="#faq-section">
      <>
        <Flex w="full" justify="space-between">
          <SectionHeading mb={0}>Frequently asked questions</SectionHeading>
        </Flex>

        <Flex m={4} mb={-4} gap={6}>
          <FAQFilterBtn
            name="All"
            active={activeFilter}
            setFilter={changeFilter}
          />
          {FaqList.map((e, index) => (
            <FAQFilterBtn
              key={index}
              name={e.heading}
              active={activeFilter}
              setFilter={changeFilter}
            />
          ))}
        </Flex>

        {displayList}
      </>
    </Section>
  );
};

export default FAQSection;

So I tried using the useEffect hook with a dependency on the changing filter (activeFilter) which would cause the component to rerender only one time but it doesn't.因此,我尝试使用useEffect挂钩,它依赖于不断变化的过滤器 (activeFilter),这会导致组件仅重新呈现一次,但事实并非如此。 I tried using the useCallback hook since setStates are asynchronous but it didn't help.我尝试使用useCallback挂钩,因为 setStates 是异步的,但它没有帮助。



With useMemo: issue seems "fixed"使用 useMemo:问题似乎“已解决”

Then I thought it had something to do with the filteredAllFaq being an array.map() which might be an "expensive/high load function" so I decided to go with useMemo hook which seemed to fixed the issue.然后我认为这与filteredAllFaq是一个array.map() ,这可能是一个“昂贵/高负载函数”,所以我决定使用useMemo挂钩,这似乎解决了这个问题。 Code below:代码如下:

  const filteredAllFaq = useMemo(() => allFaqItems.map((elem, index) => {
    const faq =
      activeFilter === Object.values(FaqList)[index].heading ||
      activeFilter === "All" ? (
        elem.length ? (
          <FAQItems
            items={elem}
            faqHeading={Object.values(FaqList)[index].heading}
          />
        ) : (
          ""
        )
      ) : (
        ""
      );
    return <Box key={index}>{faq}</Box>;
  }), [activeFilter]);

  const changeFilter = (filter: string) => {
    setActiveFilter(filter);
  };

  useEffect(() => {
    setDisplayList(filteredAllFaq);
  }, [activeFilter, filteredAllFaq]);

  console.log(activeFilter);

Even though it fixed the rerendering issue, I feel like I'm using it the wrong way and the whole component could be done much better.尽管它解决了重新渲染问题,但我觉得我使用它的方式不对,整个组件可以做得更好。

There's also a small issue after my "fix" because it seems to render/console.log(activeFilter) exactly 4 times at mount and whenever activeFilter changes.在我的“修复”之后还有一个小问题,因为它似乎在挂载时和activeFilter更改时恰好渲染/console.log(activeFilter) 4 次。 I was expecting it to render only once.我原以为它只渲染一次。

I'm new to React and have never used useMemo before.我是 React 的新手,之前从未使用useMemo I tried searching for a solution but I don't even know where my problem is.我尝试寻找解决方案,但我什至不知道我的问题出在哪里。 Any advice is very much appreciated.非常感谢任何建议。 Thank you谢谢

Instead of defining filteredAllFaq as a const in the function body, you can store its value in a useState hook.您可以将其值存储在useState挂钩中,而不是将filteredAllFaq定义为函数主体中的const The displayList state hook is useless and causes the problem. displayList状态挂钩是无用的并且会导致问题。 So replace that with所以用

const [filteredAllFaq, setFilteredAllFaq] = useState(allFaq);

When you want to change filteredAllFaq value, do it in the useEffect hook like this (not in the function body):当你想改变filteredAllFaq的值时,像这样在useEffect钩子中做(而不是在函数体中):

 useEffect(() => {
    const newFilteredAllFaq = allFaqItems.map((elem, index) => {
      const faq =
        activeFilter === Object.values(FaqList)[index].heading ||
          activeFilter === "All" ? (
          elem.length ? (
            <FAQItems
              items={elem}
              faqHeading={Object.values(FaqList)[index].heading}
            />
          ) : (
            ""
          )
        ) : (
          ""
        );
      return <Box key={index}>{faq}</Box>;
    });
    
    setFilteredAllFaq(newFilteredAllFaq);
  }, [activeFilter]);

Consider that filteredAllFaq dependency of useEffect has been removed and activeFilter dependency is enough for updating filteredAllFaq state.考虑到useEffectfilteredAllFaq依赖已被移除, activeFilter依赖足以更新filteredAllFaq状态。


As for the case that console.log is executed four times.至于console.log执行四次的情况。 These extra rerenders could be the result of reactStrictMode which is wrapped around the main project component (usually located in index.js file).这些额外的重新渲染可能是reactStrictMode的结果,它包裹在主要项目组件(通常位于 index.js 文件中)。 If you remove that wrapper, extra rerenders will be stopped.如果您删除该包装器,将停止额外的重新渲染。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM