javascript - React Context API 的 setter 工作很慢
问题描述
我的目标是制作这样的功能,例如根据子链接的展开/折叠来更改页面的背景渐变。为此,我使用了 useContext 钩子,但我注意到即使一切都按预期工作,背景渐变确实会发生变化,但它需要大约 20 秒才能重新呈现折叠/展开的子链接。我只看到一个关于 SO 的问题有同样的问题,但我真的不明白如何解决它。问题是上下文提供者的所有孩子都因为上下文中的状态而被重新渲染。我会上传一个代码框示例,但有人要求我不要这样做,所以这里是最可重现的代码片段。我怎样才能使用该功能并防止孩子重新渲染,所以一切工作得更快?请帮忙 :)
const Wrapper = styled('div')<{linksExpanded : boolean}>`
background-image: linear-gradient(180deg, pink 0.2%, #FFFFFF 3%);
background-color: white;
${({theme})=>theme.breakpoints.up('md')} {
background-image: linear-gradient(180deg, pink 1.5%, #FFFFFF 5.5%);
}
${({linksExpanded , theme})=>linksExpanded && `
background-image: linear-gradient(180deg, pink 2.5%, #FFFFFF 4.25%);
${theme.breakpoints.up('md')} {
background-image: linear-gradient(180deg, pink 6.5%, #FFFFFF 10%);
}
`}
`
const Home = () =>
{
const { linksExpanded } = useContext(HomeDataContext);
return (
<Wrapper linksExpanded={linksExpanded}>
<About />
<Links />
<Contacts />
<Gallery />
</Wrapper>
);
};
export default Home;
export const Landing: FC = () => <PageSkeleton
pageContent={
<HomeDataContextProvider><Home /></HomeDataContextProvider>}
/>;
const Links = () => {
const { currentCakes, linksExpanded, setLinksExpanded } = useContext(HomeDataContext);
const renderLinks = () => (
<S.List style={{maxHeight: !linksExpanded ? '0px' : '1000px'}}>
{currentCakes.map((el: CakeDescription, key : number) => (
<li key={el.name}>
<a href='https://google.com'>Link</a>
</li>
))}
</S.List>
);
const handleLinksExpand = () => {
setLinksExpanded(!linksExpanded);
};
return (
<S.Container>
<S.ExpandableLinkContainer>
<ExpandableLink
onClick={handleLinksExpand}
open={linksExpanded}
title='Cakes'
/>
</S.ExpandableLinkContainer>
{renderLinks()}
</S.Container>
);
};
export default Links;
最后的上下文:
export type HomeDataState = {
currentCakes: Array<CakeDescription>;
setCurrentCakes?: any;
linksExpanded: boolean;
setLinksExpanded?: any;
};
const initialValue: HomeDataState = {
currentCakes: [],
linksExpanded: false,
};
export const HomeDataContext = createContext(initialValue as HomeDataState);
export const HomeDataContextProvider: FC = ({ children }) => {
const [linksExpanded, setLinksExpanded] = useState(initialValue.linksExpanded);
const [currentCakes, setCurrentCakes] = useState<CakeDescription[]>(initialValue.cakes);
const fetchCakes = async () => {
setCurrCakes(
await myAxiosConstruct.get(process.env.api);
);
};
useEffect(() => {
fetchCakes();
}, []);
return (
<HomeDataContext.Provider
value={{
currentCakes,
linksExpanded,
setLinksExpanded,
}}
>
{children}
</HomeDataContext.Provider>
);
};
解决方案
由于您的Home
组件正在使用HomeDataContext
,因此无论更新什么属性,React 都会重新渲染整个组件,涉及每个子组件(Wrapper
, About
, Links
, Contact
, Gallery
),因此这是正确的行为。
我们在这里可以做的是将 Context 分成两部分:LinksContext
和CakesContext
,分别使用各自的 Provider ,然后重构应用程序如下:
renderLinks()
现在是一个组件 (Cakes
),只有在更新时才会重新渲染currentCakes
。以前每次状态更改都会重新渲染。- 通过包装
Cakes
,我们可以防止更新memo
时 React 重新渲染子级。Home
- 我们也应该
memo
在Contacts
和Gallery
组件中使用,以防止相同的重新渲染。
试试看,希望对你有帮助!可以在此处找到有关如何优化和良好实践的完整说明:React Context,All in One
const Wrapper = styled('div')<{linksExpanded : boolean}>`
background-image: linear-gradient(180deg, pink 0.2%, #FFFFFF 3%);
background-color: white;
${({theme})=>theme.breakpoints.up('md')} {
background-image: linear-gradient(180deg, pink 1.5%, #FFFFFF 5.5%);
}
${({linksExpanded , theme})=>linksExpanded && `
background-image: linear-gradient(180deg, pink 2.5%, #FFFFFF 4.25%);
${theme.breakpoints.up('md')} {
background-image: linear-gradient(180deg, pink 6.5%, #FFFFFF 10%);
}
`}
`
export const Home = () =>
{
const { linksExpanded, setLinksExpanded } = useContext(LinksContext);
return (
<Wrapper linksExpanded={linksExpanded}>
<About />
<Links linksExpanded={linksExpanded} setLinksExpanded={setLinksExpanded} />
<Contacts />
<Gallery />
</Wrapper>
);
};
export const Landing = () => <PageSkeleton
pageContent={
<HomeDataContextProvider>
<Home />
</HomeDataContextProvider>
} />;
export const Cakes = memo(() => {
const { currentCakes } = useContext(CakesContext)
return currentCakes.map((el, key) => (
<li key={el.name}>
<a href='https://google.com'>Link</a>
</li>
))
})
export const Links = ({ linksExpanded, setLinksExpanded }) => {
const handleLinksExpand = () => {
setLinksExpanded(!linksExpanded);
};
return (
<S.Container>
<S.ExpandableLinkContainer>
<ExpandableLink
onClick={handleLinksExpand}
open={linksExpanded}
title='Cakes'
/>
</S.ExpandableLinkContainer>
<S.List style={{maxHeight: !linksExpanded ? '0px' : '1000px'}}>
<Cakes/>
</S.List>
</S.Container>
);
};
推荐阅读
- javascript - 流,如何从混合类型转换为更具体的类型
- powershell - 无法在 Linux 遥控器上通过 Posh-SSH 执行 cd
- selenium - 如何根据 HTML 使用 xpath 识别复选框
- google-maps - Google Java Script API 的 API 密钥
- wordpress - 如何将社交媒体图标重新定位为右侧固定侧导航栏中的菜单列表项?
- php - 致命错误:未捕获的 PDOException:SQLSTATE[HY000]:一般错误:获取模式需要类名参数
- python - 缓存 Spark Dataframe 以提高速度
- c# - 为什么不能这样实现接口
- javascript - 如何使用 javascript 在任何驱动器中创建文本文件
- javascript - 从 nodejs/expressjs 中的 postman 提取二进制数据(文件)