React页面加载闪烁问题求助(含组件代码)
React页面加载时闪烁问题求助
我在React项目中遇到页面加载时闪烁的问题,每次打开页面都会出现该现象。项目仅进行数据获取操作,无复杂业务逻辑。涉及的组件包括PersonalList、Favourites、SharedRecipes,这些组件仅负责数据获取与渲染。我尝试过多种数据获取方法、加载动画、setTimeout,但均导致用户体验恶化。
PersonalList组件
import "./PersonalList.css"; import "../ErrorPage/ErrorPage.css"; import React, { useLayoutEffect, useState, useEffect, useContext, ReactMapboxGl } from 'react'; import RecipePersonalModel from "../Recipe/RecipePersonalModel/RecipePersonalModel"; import { AuthContext } from "../../contexts/AuthContext"; import * as userService from '../../services/userService'; import ErrorPage from '../../components/ErrorPage/ErrorPage'; import { debounce } from 'lodash'; const PersonalList = () => { const [recipes, setRecipes] = useState([]); const { user } = useContext(AuthContext); useLayoutEffect(() => { userService.ownRecipes(user._id) .then(recipeResult => { setRecipes(recipeResult); }); }, []); return ( <div> <h1>Your Recipes</h1> <section className="secper"> {/* { recipes.length > 0 ? <> {recipes.map(x => <RecipePersonalModel key ={x._id} recipe={x}/>)} </> : <div className="errorpaper"> <h1>No recipes found.</h1> <ErrorPage/> </div> } */} {recipes.map(x => <RecipePersonalModel key ={x._id} recipe={x}/>)} </section> </div> ); } export default PersonalList;
Favourites组件
import "./Favourites.css"; import React, { useLayoutEffect, useState ,useEffect,useContext} from 'react'; import RecipeFavourite from "../Recipe/RecipeFavourite/RecipeFavourite"; import * as userService from '../../services/userService'; import { AuthContext } from "../../contexts/AuthContext"; import ErrorPage from "../ErrorPage/ErrorPage"; function Favourites() { const [favourites,setFavourites] = useState([]); const {user} = useContext(AuthContext); const count = favourites.length; useLayoutEffect(() => { userService.yourFavourites(user._id) .then(result => { setFavourites(result); }) },[]); useLayoutEffect(() => { window.scrollTo(0, 0) }); return ( <div> <h1 className = "h1m">Your Favourite Recipes</h1> <section className="secfav"> {/* { favourites.length > 0 ? <> {favourites.map(x => <RecipeFavourite key ={x._id} recipe={x} />)} </> : <div className="errorpaper"> <h1>No recipes found.</h1> <ErrorPage/> </div> } */} {favourites.map(x => <RecipeFavourite key ={x._id} recipe={x} />)} </section> </div> ); } export default Favourites;
SharedRecipes组件
import { Link, NavLink, useNavigate, useParams } from 'react-router-dom'; import React, { useContext, useEffect, useLayoutEffect, useState } from 'react'; import RecipePersonalModel from "../Recipe/RecipeModel/RecipeModel"; import RecipeFavourite from "../Recipe/RecipeFavourite/RecipeFavourite"; import * as userService from "../../services/userService"; import RecipeShared from '../Recipe/RecipeShared/RecipeShared'; import { AuthContext } from '../../contexts/AuthContext'; import ErrorPage from '../ErrorPage/ErrorPage'; const SharedRecipes = () => { const [recipes,setRecipes] = useState([]); const [recipe,setRecipe] = useState([]); const {recipeId} = useParams(); const {user} = useContext(AuthContext); const recipesd = async () => { let reciperes = await userService.getOne(recipeId); console.log(reciperes); setRecipe(reciperes); }; useEffect(() => { recipesd(recipe); },[]); const [recipessh,setRecipessh] = useState([]); useLayoutEffect(() => { userService.getAll() .then(result => { setRecipessh(result); }) },[]); const history = useNavigate(); useLayoutEffect(() => { window.scrollTo(0, 0) }); const publicall = recipe.publicp=="yes"; return ( <div> <h1>Shared Recipes</h1> <section className="sec4"> {/* { recipessh.length > 0 ? <> {recipessh.filter(x=>x._ownerId!=user._id && x.publicp.toLowerCase()=="yes").map(x => <RecipeShared key ={x._id} recipe={x} />)} </> : <div className="errorpaper"> <h1>No recipes found.</h1> </div> } */} {recipessh.map(x=> <RecipeShared key={x._id} recipe={x} />)} </section> </div> ); } export default SharedRecipes;
问题分析与解决方案
核心原因
- 初始空状态渲染:组件初始时数据数组为空,直接渲染空列表,数据加载完成后触发二次渲染,造成视觉闪烁。
useLayoutEffect误用:数据请求属于异步操作,useLayoutEffect会同步阻塞DOM渲染,反而可能加剧闪烁;无依赖的滚动操作会在每次渲染时执行,触发不必要的重绘。- 缺失加载状态处理:原代码注释掉了空状态判断,没有加载期间的占位UI,数据从无到有的变化会形成明显闪烁。
具体修复步骤
1. 替换useLayoutEffect为useEffect处理数据请求
异步数据请求用useEffect更合适,避免阻塞DOM渲染,同时添加正确的依赖项:
// PersonalList组件示例修改 useEffect(() => { userService.ownRecipes(user._id) .then(recipeResult => setRecipes(recipeResult)); }, [user._id]);
2. 添加加载状态与占位UI
在每个组件中新增isLoading状态,加载期间显示占位内容(骨架屏/加载文字),避免空状态:
// PersonalList组件示例修改 const PersonalList = () => { const [recipes, setRecipes] = useState([]); const [isLoading, setIsLoading] = useState(true); const { user } = useContext(AuthContext); useEffect(() => { userService.ownRecipes(user._id) .then(recipeResult => setRecipes(recipeResult)) .finally(() => setIsLoading(false)); }, [user._id]); return ( <div> <h1>Your Recipes</h1> <section className="secper"> {isLoading ? ( <div className="loading">加载中...</div> ) : recipes.length > 0 ? ( recipes.map(x => <RecipePersonalModel key={x._id} recipe={x} />) ) : ( <div className="errorpaper"> <h1>No recipes found.</h1> <ErrorPage /> </div> )} </section> </div> ); };
3. 修复无依赖的滚动useLayoutEffect
给滚动操作添加空依赖数组,确保只在组件挂载时执行一次:
useLayoutEffect(() => { window.scrollTo(0, 0); }, []);
4. 可选:React 18+ 启用Suspense
如果项目使用React 18,可结合Suspense统一处理加载状态,避免组件内部重复逻辑:
// 路由层示例 import { Suspense } from 'react'; <Suspense fallback={<div>加载中...</div>}> <PersonalList /> </Suspense>
内容的提问来源于stack exchange,提问作者DimitriTfb
相关产品推荐
相关产品推荐

