React+Chakra网站响应式开发中无样式内容闪烁问题求助
解决Chakra UI useBreakpointValue导致的初始渲染闪烁问题
我在React + Chakra UI项目中开发响应式布局时碰到了问题:使用useBreakpointValue({ base: false, lg: true })判断屏幕尺寸,但钩子返回值前页面已经完成初始渲染,导致页面出现无样式内容闪烁,等钩子返回值后才重新渲染为正确样式。
问题代码
import { Box, Flex, Image, Stack, Text, useBreakpointValue, Heading } from "@chakra-ui/react"; import React from "react"; import { LoginForm } from "./login_form"; import { colors } from "theme/colors"; import { useNavigate } from 'react-router-dom'; import { RouterPaths } from "router/routerConfig"; const Login: React.FC = () => { const navigate = useNavigate(); const handleLogin = () => { console.log('Login clicked'); navigate(RouterPaths.REGISTER); // Navigate to the next page after login }; const isLargeScreen = useBreakpointValue({ base: false, lg: true }); const effectiveIsLargeScreen = typeof isLargeScreen === 'undefined' ? true : isLargeScreen; return ( <Flex direction="column" h="100vh"> {!isLargeScreen && ( <Flex direction="row" bg={colors.primary[500]} p="5"> <Heading m="auto" color="white">Drop Here</Heading> </Flex> )} <Flex direction="row" > {isLargeScreen && ( <Box w="50%" bg={colors.primary[500]} > <Stack direction="column" p="50" h="100vh" spacing={4}> <Flex alignItems="center"> <Image src="/images/White_T.png" w="14" /> <Text fontSize="30px" fontWeight="800" color="white" ml={-2}>Drop Here</Text> </Flex> <Text fontSize="40px" fontWeight="600" color="white" pt="20%" lineHeight="shorter"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod </Text> <Text minW={400} color="white"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod </Text> <Box display="flex" flexDirection="column" justifyContent="flex-end" height="100vh"> <Text fontWeight={300} color="white">© {new Date().getFullYear()} Drop Here. All rights reserved</Text> </Box> </Stack> </Box> )} <LoginForm onLogin={handleLogin} /> </Flex> </Flex> ); }; export default Login;
解决方案
方案1:使用effectiveIsLargeScreen替代所有isLargeScreen引用
你已经定义了effectiveIsLargeScreen但没用到,这个变量能在钩子返回值前提供默认值,避免初始渲染时isLargeScreen为undefined导致的错误布局。把所有条件判断里的isLargeScreen换成这个变量,初始渲染就会使用默认值,等钩子返回正确值后再更新,减少闪烁:
// ... 其他代码不变 const isLargeScreen = useBreakpointValue({ base: false, lg: true }); const effectiveIsLargeScreen = typeof isLargeScreen === 'undefined' ? true : isLargeScreen; return ( <Flex direction="column" h="100vh"> {!effectiveIsLargeScreen && ( <Flex direction="row" bg={colors.primary[500]} p="5"> <Heading m="auto" color="white">Drop Here</Heading> </Flex> )} <Flex direction="row" > {effectiveIsLargeScreen && ( <Box w="50%" bg={colors.primary[500]} > {/* 侧边栏内容不变 */} </Box> )} <LoginForm onLogin={handleLogin} /> </Flex> </Flex> );
方案2:用Chakra UI响应式Props替代条件渲染(推荐)
条件渲染会导致DOM元素的添加/删除,这是闪烁的根本原因。Chakra UI支持直接在组件属性上写响应式值,通过CSS媒体查询控制显示/隐藏,不需要依赖useBreakpointValue,从根源解决闪烁问题:
import { Box, Flex, Image, Stack, Text, Heading } from "@chakra-ui/react"; import React from "react"; import { LoginForm } from "./login_form"; import { colors } from "theme/colors"; import { useNavigate } from 'react-router-dom'; import { RouterPaths } from "router/routerConfig"; const Login: React.FC = () => { const navigate = useNavigate(); const handleLogin = () => { console.log('Login clicked'); navigate(RouterPaths.REGISTER); }; return ( <Flex direction="column" h="100vh"> {/* 小屏幕显示,大屏幕隐藏 */} <Flex direction="row" bg={colors.primary[500]} p="5" display={{ base: 'flex', lg: 'none' }}> <Heading m="auto" color="white">Drop Here</Heading> </Flex> <Flex direction="row" > {/* 大屏幕显示,小屏幕隐藏+宽度为0 */} <Box w={{ base: 0, lg: '50%' }} bg={colors.primary[500]} display={{ base: 'none', lg: 'block' }} > <Stack direction="column" p="50" h="100vh" spacing={4}> <Flex alignItems="center"> <Image src="/images/White_T.png" w="14" /> <Text fontSize="30px" fontWeight="800" color="white" ml={-2}>Drop Here</Text> </Flex> <Text fontSize="40px" fontWeight="600" color="white" pt="20%" lineHeight="shorter"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod </Text> <Text minW={400} color="white"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod </Text> <Box display="flex" flexDirection="column" justifyContent="flex-end" height="100vh"> <Text fontWeight={300} color="white">© {new Date().getFullYear()} Drop Here. All rights reserved</Text> </Box> </Stack> </Box> <LoginForm onLogin={handleLogin} /> </Flex> </Flex> ); }; export default Login;
这种方式下,所有元素一开始就存在于DOM中,只是通过CSS媒体查询根据屏幕尺寸控制显示/隐藏,不会有两次渲染的过程,彻底消除闪烁。
内容的提问来源于stack exchange,提问作者Sayuru_Sandaru
相关产品推荐
相关产品推荐

