我在成功编译下面的代码时遇到问题,但无限次地重新渲染组件,并且在控制台中收到此错误:
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;
因此,我尝试使用
useEffect
钩子,并依赖于更改过滤器 (activeFilter),这会导致组件仅重新渲染一次,但事实并非如此。我尝试使用 useCallback
钩子,因为 setStates 是异步的,但没有帮助。
然后我认为这与
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
发生变化时渲染/console.log(activeFilter) 正好 4 次。我原以为它只会渲染一次。
我是 React 新手,之前从未使用过
useMemo
。我尝试寻找解决方案,但我什至不知道我的问题出在哪里。非常感谢任何建议。谢谢你
您可以将其值存储在
filteredAllFaq
钩子中,而不是在函数体中将 const
定义为 useState
。 displayList
状态钩子是无用的并且会导致问题。所以将其替换为
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]);
考虑到
filteredAllFaq
的 useEffect
依赖项已被删除,并且 activeFilter
依赖项足以更新 filteredAllFaq
状态。
对于
console.log
执行四次的情况。这些额外的重新渲染可能是 reactStrictMode
的结果,它包裹着主项目组件(通常位于 index.js 文件中)。如果删除该包装器,额外的重新渲染将停止。