From ca02392d540eb748ebbc0082f6676f087c1be442 Mon Sep 17 00:00:00 2001 From: cshahu Date: Wed, 11 Oct 2023 00:24:11 -0700 Subject: [PATCH] Submitting the code --- src/features/photos/create/index.js | 4 ++++ src/features/photos/list/index.js | 10 +++++--- src/features/photos/photos.slice.js | 12 ++++++++++ src/features/search/search-bar/index.js | 2 ++ src/features/suggestion/index.js | 8 +++++-- src/features/suggestion/suggestion.slice.js | 26 +++++++++++++++++++-- 6 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/features/photos/create/index.js b/src/features/photos/create/index.js index d988b0a..35bf61a 100644 --- a/src/features/photos/create/index.js +++ b/src/features/photos/create/index.js @@ -1,13 +1,16 @@ import { useState } from 'react'; // Task 2: Import the `useDispatch()` method from the appropriate package +import { useDispatch } from 'react-redux'; // Task 3: Import the `addPhoto()` action creator from the photos slice +import { addPhoto } from '../photos.slice'; import './create.css'; export default function CreatePhoto() { const [formData, setFormData] = useState({ imageUrl: '', caption: '' }); // Task 4: Store a reference to the Redux store's dispatch method in a variable called `dispatch` + const dispatch = useDispatch(); function handleChange({ target: { name, value } }) { setFormData({ @@ -19,6 +22,7 @@ export default function CreatePhoto() { function handleSubmit(event) { event.preventDefault(); // Task 5: Dispatch the `addPhoto()` action creator, passing in the form data + dispatch(addPhoto(formData)); setFormData({ imageUrl: '', caption: '' }); } diff --git a/src/features/photos/list/index.js b/src/features/photos/list/index.js index 3ed9ea5..0f3a5ca 100644 --- a/src/features/photos/list/index.js +++ b/src/features/photos/list/index.js @@ -1,18 +1,22 @@ import { useSelector, useDispatch } from 'react-redux'; import { // Task 7: Import the `removePhoto()` action creator from the photos slice - selectAllPhotos, + removePhoto, // Task 13: Import the `selectFilteredPhotos()` selector from the photos slice + selectFilteredPhotos, } from '../photos.slice'; import './list.css'; export default function PhotosList() { // Task 14: Call `useSelector()` below with `selectFilteredPhotos` instead of `selectAllPhotos` - const photos = useSelector(selectAllPhotos); + const photos = useSelector(selectFilteredPhotos); // Task 8: Store a reference to the Redux store's dispatch method in a variable called `dispatch` + const dispatch = useDispatch(); - function handleDeleteButtonClick(id) { + + function handleDeleteButtonClick(photoId) { // Task 9: Dispatch the `removePhoto()` action creator, passing in the id + dispatch(removePhoto({ id: photoId })); } const photosListItems = photos.map(({ id, caption, imageUrl }) => ( diff --git a/src/features/photos/photos.slice.js b/src/features/photos/photos.slice.js index 50985e8..4f7d3b6 100644 --- a/src/features/photos/photos.slice.js +++ b/src/features/photos/photos.slice.js @@ -13,10 +13,20 @@ const options = { // Task 1: Create an `addPhoto()` case reducer that adds a photo to state.photos. // Task 1 Hint: You can use state.photos.unshift() // `unshift()` documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift + addPhoto: (state, action) => { + state.photos.unshift(action.payload); + }, // Task 6: Create an `removePhoto()` case reducer that removes a photo from state.photos // Task 6 Hint: You can use state.photos.splice() // `splice()` documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice + // Task 6: Create a `removePhoto()` case reducer that removes a photo from state.photos. + removePhoto: (state, action) => { + const index = state.photos.findIndex(photo => photo.id === action.payload.id); + if (index !== -1) { + state.photos.splice(index, 1); + } + } }, }; @@ -29,4 +39,6 @@ export default photosSlice.reducer; export const selectAllPhotos = (state) => state.photos.photos; export const selectFilteredPhotos = (state) => { // Task 12: Complete `selectFilteredPhotos()` selector to return a filtered list of photos whose captions match the user's search term + const searchTerm = selectSearchTerm(state); + return state.photos.photos.filter(photo => photo.caption.toLowerCase().includes(searchTerm.toLowerCase())); }; diff --git a/src/features/search/search-bar/index.js b/src/features/search/search-bar/index.js index 0169c64..14a8038 100644 --- a/src/features/search/search-bar/index.js +++ b/src/features/search/search-bar/index.js @@ -5,9 +5,11 @@ import './search-bar.css'; export default function SearchBar() { const searchTerm = useSelector(selectSearchTerm); // Task 10: Store a reference to the Redux store's dispatch method in a variable called `dispatch` + const dispatch = useDispatch(); function handleChange({ target: { value } }) { // Task 11: Dispatch the `setSearchTerm()` action creator, passing in the value of the search input + dispatch(setSearchTerm(value)); } return ( diff --git a/src/features/suggestion/index.js b/src/features/suggestion/index.js index e41c18c..95c9538 100644 --- a/src/features/suggestion/index.js +++ b/src/features/suggestion/index.js @@ -4,6 +4,7 @@ import { fetchSuggestion, selectError, selectLoading, + selectSuggestion // Task 18: Import the `selectSuggestion()` selector from the suggestion slice } from './suggestion.slice'; import './suggestion.css'; @@ -11,6 +12,7 @@ import './suggestion.css'; export default function Suggestion() { // Task 19: Call useSelector() with the selectSuggestion() selector // The component needs to access the `imageUrl` and `caption` properties of the suggestion object. + const suggestion = useSelector(selectSuggestion); const loading = useSelector(selectLoading); const error = useSelector(selectError); const dispatch = useDispatch(); @@ -18,6 +20,7 @@ export default function Suggestion() { useEffect(() => { async function loadSuggestion() { // Task 20: Dispatch the fetchSuggestion() action creator + dispatch(fetchSuggestion()); } loadSuggestion(); }, [dispatch]); @@ -29,10 +32,11 @@ export default function Suggestion() { render =

Sorry, we're having trouble loading the suggestion.

; } else { // Task 21: Enable the two JSX lines below needed to display the suggestion on the page + const { imageUrl, caption } = suggestion; render = ( <> - {/* {caption} -

{imageUrl}

*/} + {caption} +

{caption}

); } diff --git a/src/features/suggestion/suggestion.slice.js b/src/features/suggestion/suggestion.slice.js index 6db58f1..51e462c 100644 --- a/src/features/suggestion/suggestion.slice.js +++ b/src/features/suggestion/suggestion.slice.js @@ -1,7 +1,15 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; export const fetchSuggestion = - createAsyncThunk(/* Task 15: Complete the `createAsyncThunk()` function to load a suggestion from this URL: http://localhost:3004/api/suggestion */); + createAsyncThunk(/* Task 15: Complete the `createAsyncThunk()` function to load a suggestion from this URL: http://localhost:3004/api/suggestion */ + + 'suggestion/fetchSuggestion', + async () => { + const response = await fetch('http://localhost:3004/api/suggestion'); + return response.json(); + } + + ); const initialState = { suggestion: '', @@ -13,8 +21,21 @@ const options = { name: 'suggestion', initialState, reducers: {}, - extraReducers: { + extraReducers: builder => { /* Task 16: Inside `extraReducers`, add reducers to handle all three promise lifecycle states - pending, fulfilled, and rejected - for the `fetchSuggestion()` call */ + builder + // Task 16: Handle promise lifecycle states for fetchSuggestion call + .addCase(fetchSuggestion.pending, state => { + state.status = 'loading'; + }) + .addCase(fetchSuggestion.fulfilled, (state, action) => { + state.status = 'succeeded'; + state.suggestion = action.payload; + }) + .addCase(fetchSuggestion.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error.message; + }); }, }; @@ -23,6 +44,7 @@ const suggestionSlice = createSlice(options); export default suggestionSlice.reducer; // Task 17: Create a selector, called `selectSuggestion`, for the `suggestion` state variable and export it from the file +export const selectSuggestion = state => state.suggestion.suggestion; export const selectLoading = (state) => state.suggestion.loading; export const selectError = (state) => state.suggestion.error;