Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add/delete/search dog(s) #3

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions src/features/photos/create/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useState } from 'react';
import { useState } from "react";
import { addPhoto } from "../photos.slice";

// Task 2: Import the `useDispatch()` method from the appropriate package
// Task 3: Import the `addPhoto()` action creator from the photos slice

import './create.css';
import "./create.css";
import { useDispatch } from "react-redux";

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 [formData, setFormData] = useState({ imageUrl: "", caption: "" });
const dispatch = useDispatch();

function handleChange({ target: { name, value } }) {
setFormData({
Expand All @@ -18,8 +17,8 @@ export default function CreatePhoto() {

function handleSubmit(event) {
event.preventDefault();
// Task 5: Dispatch the `addPhoto()` action creator, passing in the form data
setFormData({ imageUrl: '', caption: '' });
dispatch(addPhoto(formData));
setFormData({ imageUrl: "", caption: "" });
}

return (
Expand Down
12 changes: 5 additions & 7 deletions src/features/photos/list/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { useSelector, useDispatch } from 'react-redux';
import {
// Task 7: Import the `removePhoto()` action creator from the photos slice
selectAllPhotos,
// Task 13: Import the `selectFilteredPhotos()` selector from the photos slice
removePhoto,
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);
// Task 8: Store a reference to the Redux store's dispatch method in a variable called `dispatch`
const photos = useSelector(selectFilteredPhotos);
const dispatch = useDispatch()

function handleDeleteButtonClick(id) {
// Task 9: Dispatch the `removePhoto()` action creator, passing in the id
dispatch(removePhoto(id))
}

const photosListItems = photos.map(({ id, caption, imageUrl }) => (
Expand Down
35 changes: 23 additions & 12 deletions src/features/photos/photos.slice.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import { createSlice } from '@reduxjs/toolkit';
import { selectSearchTerm } from '../search/search.slice';
import photos from './photos.data.js';
import { createSlice } from "@reduxjs/toolkit";
import { selectSearchTerm } from "../search/search.slice";
import photos from "./photos.data.js";

const initialState = {
photos,
};

const options = {
name: 'photos',
name: "photos",
initialState,
reducers: {
// 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

// 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
addPhoto: (state, action) => {
state.photos.push({
id: state.photos.length + 1,
caption: action.payload.caption,
imageUrl: action.payload.imageUrl,
});
},
removePhoto: (state, action) => {
state.photos.splice(
state.photos.findIndex((photo) => photo.id === action.payload),
1
);
},
},
};

Expand All @@ -28,5 +34,10 @@ 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 allPhotos = selectAllPhotos(state);
const searchTerm = selectSearchTerm(state);

return allPhotos.filter((photo) =>
photo.caption.toLowerCase().includes(searchTerm.toLowerCase())
);
};
4 changes: 2 additions & 2 deletions src/features/search/search-bar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ 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 (
Expand Down
20 changes: 9 additions & 11 deletions src/features/suggestion/index.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
fetchSuggestion,
selectError,
selectLoading,
// Task 18: Import the `selectSuggestion()` selector from the suggestion slice
} from './suggestion.slice';
import './suggestion.css';
selectSuggestion,
} from "./suggestion.slice";
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 loading = useSelector(selectLoading);
const error = useSelector(selectError);
const { imageUrl, caption } = useSelector(selectSuggestion);
const dispatch = useDispatch();

useEffect(() => {
async function loadSuggestion() {
// Task 20: Dispatch the fetchSuggestion() action creator
await dispatch(fetchSuggestion());
}
loadSuggestion();
}, [dispatch]);
Expand All @@ -28,11 +27,10 @@ export default function Suggestion() {
} else if (error) {
render = <h3>Sorry, we're having trouble loading the suggestion.</h3>;
} else {
// Task 21: Enable the two JSX lines below needed to display the suggestion on the page
render = (
<>
{/* <img alt={caption} src={imageUrl} />
<p>{imageUrl}</p> */}
<img alt={caption} src={imageUrl} />
<p>{imageUrl}</p>
</>
);
}
Expand Down
36 changes: 28 additions & 8 deletions src/features/suggestion/suggestion.slice.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
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 */);
export const fetchSuggestion = createAsyncThunk(
"suggestion/fetchSuggestion",
async (arg, thunkAPI) => {
const response = await fetch("http://localhost:3004/api/suggestion");
const { data } = await response.json();
return data;
}
);

const initialState = {
suggestion: '',
suggestion: "",
loading: false,
error: true,
};

const options = {
name: 'suggestion',
name: "suggestion",
initialState,
reducers: {},
extraReducers: {
/* Task 16: Inside `extraReducers`, add reducers to handle all three promise lifecycle states - pending, fulfilled, and rejected - for the `fetchSuggestion()` call */
[fetchSuggestion.pending]: (state) => {
state.loading = true;
state.error = false;
},
[fetchSuggestion.fulfilled]: (
state,
{ payload: { imageUrl, caption } }
) => {
state.suggestion = { imageUrl, caption };
state.loading = false;
state.error = false;
},
[fetchSuggestion.error]: (state) => {
state.error = true;
state.loading = false;
},
},
};

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 selectLoading = (state) => state.suggestion.loading;
export const selectError = (state) => state.suggestion.error;
export const selectSuggestion = (state) => state.suggestion.suggestion;