As you can see on the image, after fetching data and displaying it on the screen, orange button appears (in the center) for 1 second and then dissappears.
Code for component:
const Home: FC = () => {
const { pizzas, loading, error, count } = useAppSelector(
(state) => state.pizzas
)
const { categoryID, searchValue, currentPage, sortNameObj } =
useAppSelector((state) => state.filter)
const dispatch = useAppDispatch()
const handleChangeCategory = useCallback((index: number) => {
dispatch(setCategoryID(index))
dispatch(setCurrentPage(1))
}, [])
const handleChangePage = (page: number) => {
dispatch(setCurrentPage(page))
}
const pizzaList = pizzas?.map((item) => {
const pizzaImg = pizzaImagesMiddle[item.title]
return <PizzaCard key={item.id} item={item} pizzaImg={pizzaImg} />
})
const skeletons = [...new Array(4)].map((_, index) => (
<PizzaSkeleton key={index} />
))
const loadedPizzas = loading ? skeletons : pizzaList
useEffect(() => {
dispatch(fetchPizzas())
}, [categoryID, searchValue, sortNameObj, currentPage])
if (error) {
return <EmptyResult title='Произошла ошибка' />
}
if (!loading && (!pizzas || pizzas?.length === 0)) {
return <EmptyResult title='Пиццы не найдены' />
}
return (
<div className={styles.home__container}>
<div className={styles.content__header}>
<Categories
handleChangeCategory={handleChangeCategory}
value={categoryID}
/>
<Sort sortNameObj={sortNameObj} />
</div>
<h2>Все пиццы</h2>
<section className={styles.content__list}>{loadedPizzas}</section>
<Pagination
handleChangePage={handleChangePage}
currentPage={currentPage}
itemsLength={count}
/>
</div>
)
}
It is because cheking pizzas length in condition if (!loading && (!pizzas || pizzas?.length === 0))
. Without check of empty length if (!loading && !pizzas)
everything goes well. But I need to check if array is empty or not.
In default, pizzas length is empty (so I have empty array before fetching data)
Pizza slice:
const initialState: PizzasState = {
pizzas: [],
loading: false,
error: null,
count: 0
}
const pizzasSlice = createSlice({
name: 'pizzas',
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchPizzas.pending, (state) => {
state.loading = true;
state.pizzas = [];
state.error = null;
state.count = 0
});
builder.addCase(fetchPizzas.fulfilled, (state, action) => {
state.pizzas = action.payload.items;
state.error = null;
state.count = action.payload.count;
state.loading = false
});
builder.addCase(fetchPizzas.rejected, (state, action) => {
state.pizzas = [];
state.count = 0;
if (action.payload) {
state.error = action.payload.message
} else {
state.error = action.error.message
};
state.loading = false
})
}
})
Question: How to properly avoid flashing <EmptyResult/>
for 1 second?
2
Answers
you can either place a setTimeout(() => dispatch(fetchPizzas()), 1000) on your useEffect or, if any of those functions are async, you can use a State to conditionally render the component or not. Where you are returning the component that you want to delay, make something like: return shouldRenderPizza ? (your actual pizza component) :
now, on your useEffect make something like dispatch(fetchPizzas().then(result => setTimeout(() => setShouldRenderPizza(true), 1000)))
Issue
The
EmptyResult
component is currently displayed whenloading
is true and thepizzas
state is falsey or empty. Thepizzas
state is initially empty and is also set to[]
when thefetchPizzas
action is pending.Solution
If you only want to display
EmptyResult
once data has been loaded then select a different value from[]
– "fetched data and empty" and[....]
– "fetched data and populated" to differentiate the states and loading conditions. Usingundefined
ornull
are valid choices here to indicate that data hasn’t been fetched/loaded yet, and is easy to check in the UI.Update the UI to check for
undefined
/null
loaded data.