Commit fd19e5b6 authored by Ken's avatar Ken

feat: Add Lazy loading

parent 74b0cef4
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
"query-string": "^7.1.1", "query-string": "^7.1.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-intersection-observer": "^9.4.1",
"react-redux": "^8.0.4", "react-redux": "^8.0.4",
"react-router-dom": "^6.4.5", "react-router-dom": "^6.4.5",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
...@@ -14496,6 +14497,14 @@ ...@@ -14496,6 +14497,14 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
}, },
"node_modules/react-intersection-observer": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.1.tgz",
"integrity": "sha512-IXpIsPe6BleFOEHKzKh5UjwRUaz/JYS0lT/HPsupWEQou2hDqjhLMStc5zyE3eQVT4Fk3FufM8Fw33qW1uyeiw==",
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
......
import { IPagination } from "pages/interface";
import axiosClient from "./axiosClient"; import axiosClient from "./axiosClient";
const newsApi = { const newsApi = {
getNews: () => { getNews: (params: IPagination) => {
const url = "/api/news"; const url = "/api/news";
return axiosClient.get(url); return axiosClient.get(url, { params });
}, },
getNewsDetail: (id: string) => { getNewsDetail: (id: string) => {
const url = `/api/news/${id}`; const url = `/api/news/${id}`;
......
...@@ -11,8 +11,6 @@ const NewsDetail = () => { ...@@ -11,8 +11,6 @@ const NewsDetail = () => {
const { newsDetail } = useAppSelector(newsDetailSelector); const { newsDetail } = useAppSelector(newsDetailSelector);
const { id } = useParams(); const { id } = useParams();
console.log(newsDetail);
useEffect(() => { useEffect(() => {
dispatch(handleLoading(true)); dispatch(handleLoading(true));
...@@ -38,7 +36,7 @@ const NewsDetail = () => { ...@@ -38,7 +36,7 @@ const NewsDetail = () => {
<div <div
className="news-detail container py-4" className="news-detail container py-4"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: purifyHTML(newsDetail.newspaperContent.content), __html: purifyHTML(newsDetail.content),
}} }}
></div> ></div>
) : ( ) : (
......
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit"; import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
import newsApi from "api/newsApi"; import newsApi from "api/newsApi";
import { INewspaper } from "pages/interface"; import { INewspaper, IPagination } from "pages/interface";
const initialState: { newsData: INewspaper[] } = { const initialState: { newsData: INewspaper[] } = {
newsData: [], newsData: [],
}; };
export const getNews = createAsyncThunk("home/news", async () => { export const getNews = createAsyncThunk(
const res = await newsApi.getNews(); "home/news",
async (params: IPagination) => {
const res = await newsApi.getNews(params);
return res; return res;
}); }
);
const home = createSlice({ const home = createSlice({
name: "home", name: "home",
...@@ -17,13 +20,13 @@ const home = createSlice({ ...@@ -17,13 +20,13 @@ const home = createSlice({
reducers: {}, reducers: {},
extraReducers: (builder) => { extraReducers: (builder) => {
builder.addCase(getNews.fulfilled, (state, action: PayloadAction<any>) => { builder.addCase(getNews.fulfilled, (state, action: PayloadAction<any>) => {
const { data } = action.payload; const { collection } = action.payload.data;
const sortedDataByDate = data.sort((a: INewspaper, b: INewspaper) => { // const sortedDataByDate = data.sort((a: INewspaper, b: INewspaper) => {
return ( // return (
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() // new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
); // );
}); // });
state.newsData = sortedDataByDate; state.newsData = [...state.newsData, ...collection];
}); });
}, },
}); });
......
...@@ -2,30 +2,54 @@ import { handleLoading } from "app/globalSlice"; ...@@ -2,30 +2,54 @@ import { handleLoading } from "app/globalSlice";
import { useAppDispatch, useAppSelector } from "app/hooks"; import { useAppDispatch, useAppSelector } from "app/hooks";
import { homeSelector } from "app/selectors"; import { homeSelector } from "app/selectors";
import WrapperContainer from "components/WrapperContainer"; import WrapperContainer from "components/WrapperContainer";
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import Newspaper from "../../components/Newspaper"; import Newspaper from "../../components/Newspaper";
import { getNews } from "./homePageSlice"; import { getNews } from "./homePageSlice";
import { Button } from "@mui/material"; import { IPagination } from "pages/interface";
import { useInView } from "react-intersection-observer";
import { CircularProgress } from "@mui/material";
const HomePage = () => { const HomePage = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { newsData } = useAppSelector(homeSelector); const { newsData } = useAppSelector(homeSelector);
const { ref, inView } = useInView({
threshold: 0.7,
initialInView: false,
delay: 100,
});
const [page, setPage] = useState<number>(1);
const [isLocalLoading, setIsLocalLoading] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
dispatch(handleLoading(true));
try { try {
setIsLocalLoading(true);
const timeout = setTimeout(() => {
const newsParams: IPagination = {
Filters: "",
Sorts: "",
Page: page,
PageSize: 10,
};
const fetchData = async () => { const fetchData = async () => {
await dispatch(getNews()); await dispatch(getNews(newsParams));
}; };
fetchData(); fetchData();
setIsLocalLoading(false);
}, 1000);
} catch (err) { } catch (err) {
console.error("ERROR: ", err); console.error("ERROR: ", err);
} finally { setIsLocalLoading(false);
dispatch(handleLoading(false));
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, [page]);
useEffect(() => {
if (inView) {
setPage((page) => page + 1);
}
}, [inView]);
return ( return (
<main className="homePage"> <main className="homePage">
...@@ -41,11 +65,14 @@ const HomePage = () => { ...@@ -41,11 +65,14 @@ const HomePage = () => {
))} ))}
</div> </div>
<div className="homePage-load d-flex justify-content-center"> {newsData.length > 0 && (
<Button variant="outlined" color="primary"> <div
Xem Thêm className="homePage-load d-flex justify-content-center"
</Button> ref={ref}
>
{isLocalLoading && <CircularProgress />}
</div> </div>
)}
</WrapperContainer> </WrapperContainer>
</main> </main>
); );
......
...@@ -9,10 +9,12 @@ export interface INewspaper { ...@@ -9,10 +9,12 @@ export interface INewspaper {
createdAt: string; createdAt: string;
categorylink: string; categorylink: string;
categorylinkNavigation: ICategory; categorylinkNavigation: ICategory;
newspaperContent: INewspaperContent; content: string;
} }
export interface INewspaperContent { export interface IPagination {
id: string; Filters: string;
content: string; Sorts: string;
PageSize: number;
Page: number;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment