Flickering of the pages, only data is being fetched, no complex logic. What can cause the flickering? Everytime I open the page it happens. My data is fetched in these components they don't do anything else. I tried everything different fetch methods, loading animations, setTimeout but it makes it worse from UX point of view.
Here is a video of it: https://screenrec.com/share/xXmfehCI1B
enter link description here
This is my PersonaList component:
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;
The Favourites component:
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;
The SharedRecipes Component:
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 [favourites,setFavourites] = 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 recipesd = async () => {
// let reciperes = await userService.getOne(recipeId);
// console.log(reciperes);
// setFavourite(reciperes);
// };
// useEffect(() => { recipesd(favourite); },[]);
// const favouriter = (e) => {
// e.preventDefault();
// setFavourites(...favourites,favourite);
// console.log(favourite);
// }
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} />)}
{/* {recipessh.filter(x=>x._ownerId!=user._id && x.publicp.toLowerCase()=="yes").map(x => <RecipeShared key ={x._id} recipe={x} />)} */}
{/* that works
{recipessh.filter(x=>x._ownerId!=user._id).map(x => <RecipeShared key ={x._id} recipe={x} />)} */}
</section>
</div>
);
}
export default SharedRecipes;
CodePudding user response:
What you're observing is simply the state changing. The page just isn't styled to handle state changes in a way that conveys to the user that "data is loading".
Add a loading "spinner". This could be graphical, or for demonstration purposes here even just text. The main point here is that it just needs to be conditionally displayed.
For example, note where you have this:
userService.ownRecipes(user._id)
.then(recipeResult => {
setRecipes(recipeResult);
});
And in the UI you have this:
{favourites.map(x => <RecipeFavourite key ={x._id} recipe={x} />)}
So what does the user see when favourites
is an empty array? Nothing. Instead, show the user something.
From commented-out code, it already appears that you showed the user "No recipes found", but this wasn't ideal because then it misinforms the user. Because there are recipes, they just haven't loaded yet. So separate the concepts of "loading" from "no recipes".
Add another state value to indicate loading:
const [isLoading, setIsLoading] = useState(false);
And update it in the service operation:
setIsLoading(true);
userService.ownRecipes(user._id)
.then(recipeResult => {
setRecipes(recipeResult);
setIsLoading(false);
});
Then in your UI you can now tell the difference between three states:
- Currently loading
- Loaded, no data present
- Loaded, data present
Account for those states in your UI:
{
isLoading ?
<div>Loading recipes...</div> :
favourites.length === 0 ?
<div>No recipes found!</div> :
favourites.map(x => <RecipeFavourite key ={x._id} recipe={x} />)
}
From there it's just a matter of styling those three distinct page states however you like. You can replace the <div>
elements here with animated images (the term "loading spinner" can help you find some online), better styling, etc.
Overall the point is just that the component needs to be able to handle all three states for a complete user experience.