diff --git a/src/pages/SearchPage.jsx b/src/pages/SearchPage.jsx index b929045..899d7d4 100644 --- a/src/pages/SearchPage.jsx +++ b/src/pages/SearchPage.jsx @@ -1,51 +1,31 @@ -import { ReactComponent as IconSearch } from 'assets/images/search.svg'; -import ErrorMessage from 'components/ErrorMessage'; -import Loader from 'components/Loader'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { Link, useLocation, useSearchParams } from 'react-router-dom'; -import { findPostById } from 'services/api'; -// (async () => { // -- IIFE (Immediately invoked function expression) -// try { -// setIsLoading(true); -// const postData = await findPostById(query); +import Loader from 'components/Loader'; +import ErrorMessage from 'components/ErrorMessage'; + +import { requestPosts } from 'redux/postsReducer'; -// setPosts([postData]); -// } catch (error) { -// setError(error.message); -// } finally { -// setIsLoading(false); -// } -// })() +import { ReactComponent as IconSearch } from 'assets/images/search.svg'; const SearchPage = () => { // /search?query=56 const [searchParams, setSearchParams] = useSearchParams(); const location = useLocation(); - const [posts, setPosts] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); + + const posts = useSelector(state => state.postsData.posts); + const isLoading = useSelector(state => state.postsData.isLoading); + const error = useSelector(state => state.postsData.error); + const dispatch = useDispatch(); const query = searchParams.get('query'); useEffect(() => { if (!query) return; - const fetchAllPosts = async () => { - try { - setIsLoading(true); - const postData = await findPostById(query); - - setPosts([postData]); - } catch (error) { - setError(error.message); - } finally { - setIsLoading(false); - } - }; - - fetchAllPosts(); - }, [query]); + dispatch(requestPosts(query)); + }, [query, dispatch]); const handleFormSubmit = event => { event.preventDefault(); diff --git a/src/redux/postDetailReducer.js b/src/redux/postDetailReducer.js index 4a0dbba..bfbad28 100644 --- a/src/redux/postDetailReducer.js +++ b/src/redux/postDetailReducer.js @@ -31,7 +31,6 @@ const INITIAL_STATE = { postDetailsData: null, isLoading: false, error: null, - posts: [], }; const postDetailsSlice = createSlice({ diff --git a/src/redux/postReducer.js b/src/redux/postReducer.js deleted file mode 100644 index 8adf5ee..0000000 --- a/src/redux/postReducer.js +++ /dev/null @@ -1,33 +0,0 @@ -import { createAsyncThunk } from '@reduxjs/toolkit'; -import { findPostById } from 'services/api'; - -// !==============================DAL (Data Accsess Layer)================= -// створюємо асинхрону санку, яка приймає в собі два обов'язкові агрументи: -// 1) Так званий префікс санки, тобто кожна THUNK має мати унікальний префікс -// 2) Асинхрона колбек функція 'async () => {}', дана функція приймає якісь дані (data) та thunkApi - -export const requestPostDetails = createAsyncThunk( - 'postDetails/get', - - async (postId, thunkApi) => { - try { - // робимо мережевий запит - const postData = await findPostById(postId); - return postData; //Буде записано в action.payload - } catch (error) { - // після невдалого запиту ми повернемо thunkApi та викличемо спеціальний метод rejectWithVale і передамо об'єкт помилки - return thunkApi.rejectWithValue(error.message); - // rejected - виникне тільки коли ми руками переведемо проміс - } - } -); - -// !========================End/ DAL ================================= - -// створюємо початковий стан state -const INITIAL_STATE = { - postDetailsData: null, - isLoading: false, - error: null, - posts: [], -}; diff --git a/src/redux/postsReducer.js b/src/redux/postsReducer.js new file mode 100644 index 0000000..68a9896 --- /dev/null +++ b/src/redux/postsReducer.js @@ -0,0 +1,66 @@ +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; +import { findPostById } from 'services/api'; + +// !==============================DAL (Data Accsess Layer)================= +// створюємо асинхрону санку, яка приймає в собі два обов'язкові агрументи: +// 1) Так званий префікс санки, тобто кожна THUNK має мати унікальний префікс +// 2) Асинхрона колбек функція 'async () => {}', дана функція приймає якісь дані (data) та thunkApi + +export const requestPosts = createAsyncThunk( + 'posts/get', + + async (query, thunkApi) => { + try { + // робимо мережевий запит + const searchedPosts = await findPostById(query); + return searchedPosts; //Буде записано в action.payload + } catch (error) { + // після невдалого запиту ми повернемо thunkApi та викличемо спеціальний метод rejectWithVale і передамо об'єкт помилки + return thunkApi.rejectWithValue(error.message); + // rejected - виникне тільки коли ми руками переведемо проміс + } + } +); + +// !========================End/ DAL ================================= + +// створюємо початковий стан state +const INITIAL_STATE = { + posts: null, + isLoading: false, + error: null, +}; + +// Slice -створюємо новий слайс +const postsSlice = createSlice({ + // встановлюємо ім'я слайсу + name: 'posts', + // встановлюємо початковий стан ред'юсера слайсу + initialState: INITIAL_STATE, + // створюємо об'єкт ред'юсерів + // !===========================REDUX BLL=========================== + // thunk повертає спеціальні ред'юсери які отримують builder.addCase та назву thank + extraReducers: builder => + builder + // PENDING - стан очікування запиту + .addCase(requestPosts.pending, (state, action) => { + state.isLoading = true; + state.error = null; + }) + // FULFILED - запит пройшов успішно, та повернув дані + .addCase(requestPosts.fulfilled, (state, action) => { + state.isLoading = false; + // в Reducer дані потрапляють тільки через один спосіб -- action.payload + state.posts = [action.payload]; + // [action.payload] = запихаємо в масив бо в санку searchedPosts прийде тільки один пост, із-за api, замість об'єкту + }) + // REJECTED - виникла помилка під час запису + .addCase(requestPosts.rejected, (state, action) => { + state.isLoading = false; + state.error = action.payload; + }), + // !=====================End/ REDUX BLL=========================== +}); + +// ред'юсер слайсу +export const postsReducer = postsSlice.reducer; diff --git a/src/redux/store.js b/src/redux/store.js index a28dcfd..093f835 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -11,6 +11,7 @@ import { } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; import { postDetailsReducer } from './postDetailReducer'; +import { postsReducer } from './postsReducer'; const postDetailsConfig = { key: 'postDetails', @@ -22,9 +23,11 @@ const postDetailsConfig = { }; // configureStore приймає об'єкт опцій +// прикріпляємо ред'юсер до стору export const store = configureStore({ reducer: { postDetails: persistReducer(postDetailsConfig, postDetailsReducer), + postsData: postsReducer, }, middleware: getDefaultMiddleware => getDefaultMiddleware({