post api, redux optimization

This commit is contained in:
VividTruthKeeper 2023-02-10 13:44:13 +05:00
parent 0f31f32e5e
commit 69819cb908
11 changed files with 213 additions and 95 deletions

View File

@ -1,5 +1,12 @@
// Types
import { INewsScroll, INewsScrollAction } from "../types/store.types";
import {
INewsScroll,
INewsScrollAction,
IPostData,
IPostDataAction,
} from "../types/store.types";
// NewsScroll
export const setNewsScroll = (
data: INewsScroll["data"]
@ -7,3 +14,10 @@ export const setNewsScroll = (
type: "SET_NEWS_SCROLL",
payload: data,
});
// Post
export const setPost = (data: IPostData["data"]): IPostDataAction => ({
type: "SET_POST",
payload: data,
});

View File

@ -15,7 +15,7 @@ import { newsScrollParams } from "../../api/params";
// Types
import { IPostsData } from "../../types/data.types";
import { ILanguage, RootState } from "../../types/store.types";
import { RootState } from "../../types/store.types";
// Actions
import { setNewsScroll } from "../../actions/setData";
@ -30,13 +30,13 @@ const NewsScroll = ({ title }: Props) => {
const [lastLanguage, setLastLanguage] = useState<string>(language);
// redux
const data = useSelector<RootState, RootState["dataReducer"]["data"]>(
(state) => state.dataReducer.data
const data = useSelector<RootState, RootState["newsScroll"]["data"]>(
(state) => state.newsScroll.data
);
const dispatch = useDispatch();
useEffect(() => {
if (!(data[0].id > -1 && lastLanguage === language)) {
if (!((data as IPostsData[])[0].id > -1 && lastLanguage === language)) {
api.get(data, (data: IPostsData[]) => dispatch(setNewsScroll(data)));
setLastLanguage(language);
}
@ -54,7 +54,7 @@ const NewsScroll = ({ title }: Props) => {
) : null}
<div className="news-scroll-inner">
{data ? (
data.map((dataEl) => {
(data as IPostsData[]).map((dataEl) => {
return (
<News
key={uuidv4()}

View File

@ -1,15 +1,15 @@
// Modules
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination, Autoplay } from 'swiper';
import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation, Pagination, Autoplay } from "swiper";
// Images
import Placeholder from '../../assets/images/placeholder3.png';
import Placeholder from "../../assets/images/placeholder3.png";
// Components
import ContentItem from './ContentItem';
import ContentItem from "./ContentItem";
// Static
import { titlePlaceholder } from './MainContent';
import { titlePlaceholder } from "./MainContent";
const ContentSlider = () => {
return (
@ -24,8 +24,10 @@ const ContentSlider = () => {
}}
modules={[Navigation, Pagination, Autoplay]}
pagination={{
type: 'bullets',
}}>
type: "bullets",
clickable: true,
}}
>
<SwiperSlide>
<ContentItem type="big" img={Placeholder} title={titlePlaceholder} />
</SwiperSlide>

View File

@ -3,14 +3,12 @@ import { LazyLoadImage } from "react-lazy-load-image-component";
interface IProps {
img: string;
title: string;
}
const NewsArticleImg = ({ img, title }: IProps) => {
const NewsArticleImg = ({ img }: IProps) => {
return (
<div className={"news-article-image"}>
<LazyLoadImage src={img} alt="" effect="blur" useIntersectionObserver />
<div className="swiper-lazy-preloader"></div>
</div>
);
};

View File

@ -1,16 +1,18 @@
// Modules
import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation, Pagination, Autoplay } from "swiper";
import { v4 as uuidv4 } from "uuid";
// Components
import NewsArticleImg from "./NewsArticleImg";
// Images
import Placeholder from "../../assets/images/placeholder3.png";
import { IPostData } from "../../types/store.types";
import { titlePlaceholder } from "../main/MainContent";
interface IProps {
images: IPostData["data"]["data"]["featured_images"];
}
const NewsArticleSlider = () => {
const NewsArticleSlider = ({ images }: IProps) => {
return (
<div className="news-article-slider">
<Swiper
@ -24,20 +26,14 @@ const NewsArticleSlider = () => {
modules={[Navigation, Pagination, Autoplay]}
pagination={{
type: "bullets",
clickable: true,
}}
>
<SwiperSlide>
<NewsArticleImg img={Placeholder} title={titlePlaceholder} />
</SwiperSlide>
<SwiperSlide>
<NewsArticleImg img={Placeholder} title={titlePlaceholder} />
</SwiperSlide>
<SwiperSlide>
<NewsArticleImg img={Placeholder} title={titlePlaceholder} />
</SwiperSlide>
<SwiperSlide>
<NewsArticleImg img={Placeholder} title={titlePlaceholder} />
{images.map((img) => (
<SwiperSlide key={uuidv4()}>
<NewsArticleImg img={img.path} />
</SwiperSlide>
))}
</Swiper>
</div>
);

View File

@ -1,75 +1,93 @@
// Modules
import { LazyLoadImage } from "react-lazy-load-image-component";
import { Link, useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { v4 as uuidv4 } from "uuid";
// Components
import Aside from "../components/aside/Aside";
import NewsArticleSlider from "../components/news/NewsArticleSlider";
// Images
import placeholder from "../assets/images/placeholder3.png";
import Loader from "../components/global/Loader";
// Icons
import { ReactComponent as Share } from "../assets/icons/share.svg";
// Types
import { RootState } from "../types/store.types";
import { IPostData } from "../types/store.types";
// Actions
import { setPost } from "../actions/setData";
// Api
import { Api } from "../api/Api";
import { url } from "../url";
const NewsArticle = () => {
const { id } = useParams();
// api
const api = new Api(`${url}/posts/${id}`);
const language = api.language;
const [lastLanguage, setLastLanguage] = useState<string>(language);
// redux
const data = useSelector<RootState, RootState["post"]["data"]>(
(state) => state.post.data
);
const dispatch = useDispatch();
useEffect(() => {
if (
!(data.data.id === parseInt(id as string) && lastLanguage === language)
) {
api.get(data, (data: IPostData["data"]) => dispatch(setPost(data)));
setLastLanguage(language);
}
}, [language, lastLanguage]);
return (
<div className="news-article">
<div className="container">
<div className="news-article-inner">
{data.data.id > -1 ? (
<div className="news-article-content">
<div className="news-article-info">
<div className="news-article-status">
<h3 className="news-article-category">Спорт</h3>
<h3 className="news-article-date">15:23, 21 января 2023</h3>
<div className="news-article-left">
{data.data.categories.map((category) => (
<Link
key={uuidv4()}
to={`/category/${category.id}`}
className="news-article-category"
>
{category.name}
</Link>
))}
</div>
<h2 className="news-article-title">
В федерации футбола Туркменистана подвели итоги прошедшего года
и наметили курс на 2023 год
</h2>
<div className="news-article-right">
<h3 className="news-article-date">
{data.data.published_at}
</h3>
</div>
</div>
<h2 className="news-article-title">{data.data.title}</h2>
</div>
<div className="news-article-slider-wrapper">
<NewsArticleSlider />
<NewsArticleSlider images={data.data.featured_images} />
</div>
<p className="news-article-text">
Lorem ipsum dolor sit amet consectetur. Vestibulum eget elementum
urna tincidunt diam commodo mauris ac sodales. Lorem ipsum dolor
sit amet consectetur. Lorem ipsum dolor sit amet consectetur.
Lorem ipsum dolor sit amet consectetur. Vestibulum eget elementum
urna tincidunt diam commodo mauris ac sodales. Lorem ipsum dolor
sit amet consectetur. <br />
<br /> Lorem ipsum dolor sit amet consectetur. Lorem ipsum dolor
sit amet consectetur. Vestibulum eget elementum urna tincidunt
diam commodo mauris ac sodales. Lorem ipsum dolor sit amet
consectetur. Lorem ipsum dolor sit amet consectetur. Lorem ipsum
dolor sit amet consectetur. Vestibulum eget elementum urna
tincidunt diam commodo mauris ac sodales.
<br />
<br /> Lorem ipsum dolor sit amet consectetur. Vestibulum eget
elementum urna tincidunt diam commodo mauris ac sodales. Lorem
ipsum dolor sit amet consectetur. Lorem ipsum dolor sit amet
consectetur. Lorem ipsum dolor sit amet consectetur.
<br />
<br /> Vestibulum eget elementum urna tincidunt diam commodo
mauris ac sodales. Lorem ipsum dolor sit amet consectetur. Lorem
ipsum dolor sit amet consectetur. Lorem ipsum dolor sit amet
consectetur. Vestibulum eget elementum urna tincidunt diam commodo
mauris ac sodales.
<br />
<br /> Lorem ipsum dolor sit amet consectetur. Vestibulum eget
elementum urna tincidunt diam commodo mauris ac sodales. Lorem
ipsum dolor sit amet consectetur. Lorem ipsum dolor sit amet
consectetur. Lorem ipsum dolor sit amet consectetur.
<br />
<br /> Vestibulum eget elementum urna tincidunt diam commodo
mauris ac sodales. Lorem ipsum dolor sit amet consectetur. Lorem
ipsum dolor sit amet consectetur. Lorem ipsum dolor sit amet
consectetur. Vestibulum eget elementum urna tincidunt diam commodo
mauris ac sodales.
</p>
<p
className="news-article-text"
dangerouslySetInnerHTML={{ __html: data.data.content_html }}
></p>
<button className="share-btn">
<Share /> <span>Поделиться</span>
</button>
</div>
) : (
<Loader />
)}
<Aside />
</div>
</div>

View File

@ -1,7 +1,12 @@
// Types
import { INewsScroll, INewsScrollAction } from "../types/store.types";
import {
INewsScroll,
INewsScrollAction,
IPostData,
IPostDataAction,
} from "../types/store.types";
export const postsInitialState = {
export const newsScrollInitialState = {
data: [
{
id: -1,
@ -46,8 +51,8 @@ export const postsInitialState = {
],
};
export const dataReducer = (
state: INewsScroll = postsInitialState,
export const newsScrollReducer = (
state: INewsScroll = newsScrollInitialState,
action: INewsScrollAction
) => {
switch (action.type) {
@ -59,3 +64,62 @@ export const dataReducer = (
}
}
};
export const postInitialState = {
data: {
data: {
id: -1,
title: "",
slug: "",
excerpt: "",
published_at: "",
featured_images: [
{
id: -1,
disk_name: "",
file_name: "",
path: "",
extension: "",
},
{
id: -1,
disk_name: "",
file_name: "",
path: "",
extension: "",
},
{
id: -1,
disk_name: "",
file_name: "",
path: "",
extension: "",
},
],
content_html: "",
categories: [
{
id: -1,
name: "",
},
],
powerseo_title: "",
powerseo_description: "",
powerseo_keywords: "",
},
},
};
export const postReducer = (
state: IPostData = postInitialState,
action: IPostDataAction
) => {
switch (action.type) {
case "SET_POST": {
return { ...state, data: action.payload };
}
default: {
return state;
}
}
};

View File

View File

@ -4,12 +4,13 @@ import { combineReducers, configureStore } from "@reduxjs/toolkit";
// Reducers
import { activeLinkReducer } from "../reducers/activeLink.reducer";
import { languageReducer } from "../reducers/language.reducer";
import { dataReducer } from "../reducers/dataReducer";
import { newsScrollReducer, postReducer } from "../reducers/dataReducer";
export const allReducers = combineReducers({
activeLink: activeLinkReducer,
language: languageReducer,
dataReducer,
newsScroll: newsScrollReducer,
post: postReducer,
});
export const functionalityStore = configureStore({

View File

@ -20,7 +20,14 @@
.news-article-status {
display: flex;
gap: 3.2rem;
gap: 2.4rem;
align-items: center;
justify-content: space-between;
}
.news-article-left {
display: flex;
gap: 2.4rem;
align-items: center;
}
@ -47,6 +54,7 @@
}
.news-article-image {
min-height: 30rem;
img {
width: 100%;
height: 100%;

View File

@ -4,6 +4,7 @@ import { allReducers } from "../store/functionality";
// Types
import { IPostsData } from "./data.types";
// NavLink
export interface ActiveLinkType {
active: number;
}
@ -12,6 +13,7 @@ export interface ActiveLinkActionType {
payload: number;
}
// Language
export interface ILanguage {
title: "RU" | "EN" | "TM";
}
@ -21,6 +23,8 @@ export interface ILanguageAction {
payload: "RU" | "EN" | "TM";
}
// NewsScroll
export interface INewsScroll {
data: IPostsData[];
}
@ -29,6 +33,19 @@ export interface INewsScrollAction {
payload: INewsScroll["data"];
}
// Post
export interface IPostData {
data: {
data: IPostsData;
};
}
export interface IPostDataAction {
type: "SET_POST";
payload: IPostData["data"];
}
// ALL TYPES BEFORE THIS LINE ==================
export type RootState = ReturnType<typeof allReducers>;