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?
The EmptyResult
component is currently displayed when loading
is true and the pizzas
state is falsey or empty. The pizzas
state is initially empty and is also set to []
when the fetchPizzas
action is pending.
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. Using undefined
or null
are valid choices here to indicate that data hasn't been fetched/loaded yet, and is easy to check in the UI.
const initialState: PizzasState = {
pizzas: undefined, // <-- initially undefined
loading: false,
error: null,
count: 0,
};
const pizzasSlice = createSlice({
name: 'pizzas',
initialState,
extraReducers: (builder) => {
builder.addCase(fetchPizzas.pending, (state) => {
state.loading = true;
// state.pizzas = []; // <-- Don't update yet
state.error = null;
state.count = 0
});
builder.addCase(fetchPizzas.fulfilled, (state, action) => {
state.pizzas = action.payload.items; // <-- update to "loaded" value
state.error = null;
state.count = action.payload.count;
state.loading = false;
});
builder.addCase(fetchPizzas.rejected, (state, action) => {
state.pizzas = []; // <-- update to "loaded" value
state.count = 0;
state.error = action.payload
? action.payload.message
: action.error.message;
state.loading = false;
});
},
});
Update the UI to check for undefined
/null
loaded data.
const skeletons = [...new Array(4)].map((_, index) => (
<PizzaSkeleton key={index} />
));
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));
}, []);
useEffect(() => {
dispatch(fetchPizzas())
}, [categoryID, searchValue, sortNameObj, currentPage])
const handleChangePage = (page: number) => {
dispatch(setCurrentPage(page));
};
if (error) {
return <EmptyResult title='Произошла ошибка' />;
}
// Check if pizzas is a defined array and empty
if (!loading && (Array.isArray(pizzas) && !pizzas.length)) {
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}>
{loading
? skeletons
: pizzas?.map((item) => (
<PizzaCard
key={item.id}
item={item}
pizzaImg={pizzaImagesMiddle[item.title]}
/>
))
}
</section>
<Pagination
handleChangePage={handleChangePage}
currentPage={currentPage}
itemsLength={count}
/>
</div>
);
};