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



不用备忘录:无限重渲染组件

我在下面的代码成功编译时遇到了问题,但无限次重新渲染组件,我在控制台中收到此错误: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;

所以我尝试使用依赖于更改过滤器 (activeFilter) 的useEffect钩子,这会导致组件只重新渲染一次,但事实并非如此。我尝试使用useCallback钩子,因为 setState 是异步的,但它没有帮助。



使用备忘录:问题似乎"已修复">

然后我认为这与filteredAllFaq是一个array.map()有关,这可能是一个"昂贵/高负载功能",所以我决定使用useMemo钩子,这似乎解决了这个问题。下面的代码:

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);

尽管它解决了重新渲染问题,但我觉得我以错误的方式使用它,整个组件可以做得更好。

在我的"修复"之后还有一个小问题,因为它似乎在挂载时和每当activeFilter更改时渲染/控制台.log(activeFilter)正好 4 次。我预计它只会渲染一次。

我是 React 的新手,以前从未使用过useMemo。我尝试寻找解决方案,但我什至不知道我的问题在哪里。任何建议都非常感谢。谢谢

您可以将filteredAllFaq的值存储在useState钩子中,而不是在函数体中将其定义为constdisplayList状态挂钩是无用的,会导致问题。所以把它换成

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

当你想更改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]);

请考虑filteredAllFaquseEffect依赖项已被删除,并且activeFilter依赖项足以更新filteredAllFaq状态。


至于console.log被执行四次的情况。这些额外的重新渲染可能是围绕主项目组件(通常位于索引.js文件中)包装的reactStrictMode的结果。如果删除该包装器,将停止额外的重新渲染。

最新更新