diff --git a/README.md b/README.md index f8ae4e560..c45258a65 100755 --- a/README.md +++ b/README.md @@ -27,35 +27,35 @@ In this project, you take a fairly complex application used to search a movie da #### Setup Redux > *The DOM and movie reducer has been provided for you, but it's up to to connect it to redux...* -* [ ] In index.js, make use of the createStore method and Provider component to link your App to redux. +* [x ] In index.js, make use of the createStore method and Provider component to link your App to redux. #### Connecting the Movie reducer > *Within the reducers folder is the movieReducers file. We have the state already setup it up here with some initial data. Let's connect that state to our component.* -* [ ] **In movieReducer.js, make sure that we are setting our state by default to initialState.** Otherwise your state will not have the original structure it needs to function! +* [x ] **In movieReducer.js, make sure that we are setting our state by default to initialState.** Otherwise your state will not have the original structure it needs to function! -* [ ] **The MovieList component prints all of our movies to the screen.** Use the connect method here to map the movies state value into props. Replace our static movie variable with that prop. +* [ x] **The MovieList component prints all of our movies to the screen.** Use the connect method here to map the movies state value into props. Replace our static movie variable with that prop. -* [ ] **The Movie component needs to access our list of movies to function.** Map movies to props here as well. +* [ --] **The Movie component needs to access our list of movies to function.** Map movies to props here as well. -* [ ] **Finally, MovieHeader uses appTitle to display the title text.** Connect this component to appTitle and test appTitle is correctly displayed in your app. +* [x ] **Finally, MovieHeader uses appTitle to display the title text.** Connect this component to appTitle and test appTitle is correctly displayed in your app. #### Connecting the Delete and Add Movie actions > *Looks like you got a good handle on mapping stateToProps! Now let's connect some actions.* -* [ ] Note that the deleteMovie reducer case and action creator are already available. +* [x ] Note that the deleteMovie reducer case and action creator are already available. -* [ ] **We can delete movies within the Movie Component.** Connect the deleteMovie action through the connect method. +* [x ] **We can delete movies within the Movie Component.** Connect the deleteMovie action through the connect method. -* [ ] **Find the HTML element that should trigger a deletion in the movie component.** Create and connect the necessary event handlers to call deleteMovie on the current movie's id. After setting the state, redirect the user using the push('/movies') command. +* [x ] **Find the HTML element that should trigger a deletion in the movie component.** Create and connect the necessary event handlers to call deleteMovie on the current movie's id. After setting the state, redirect the user using the push('/movies') command. -* [ ] Add in an ADD_MOVIE case to movieReducer.js. +* [ x] Add in an ADD_MOVIE case to movieReducer.js. * [ ] Make this new case return a version of state with new movie values passed in through the payload. -* [ ] Create an action creator for addMovie in movieActions.js. -* [ ] Find the component that triggers the adding of a movie and connect the addMovie action. -* [ ] Create and connect the necessary event handlers to call addMovie. -* [ ] Add in push('/movies/) after calling your action to trigger a redirect. +* [ x] Create an action creator for addMovie in movieActions.js. +* [x ] Find the component that triggers the adding of a movie and connect the addMovie action. +* [x ] Create and connect the necessary event handlers to call addMovie. +* [x ] Add in push('/movies/) after calling your action to trigger a redirect. #### Build out the favorites reducer > *Alright! Now that the movie reducer is complete, you have the chance to build a reducer from scratch to handle favorite movie functionality. We will also work on combining reducers.* diff --git a/package-lock.json b/package-lock.json index 5ddc4bd0c..f84f178a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,9 @@ "nanoid": "3.1.30", "react": "17.0.2", "react-dom": "17.0.2", - "react-redux": "^7.2.6", + "react-redux": "^7.2.9", "react-router-dom": "5.2.0", - "redux": "^4.2.0", + "redux": "^4.2.1", "sass": "^1.32.8", "style-loader": "^3.3.1", "styled-components": "5.3.3" @@ -35,6 +35,7 @@ "@testing-library/jest-dom": "5.15.0", "@testing-library/react": "12.1.2", "@types/jest": "27.0.2", + "@types/react-router-dom": "^5.3.3", "babel-loader": "8.2.3", "babel-plugin-styled-components": "1.13.3", "concurrently": "6.3.0", @@ -2890,6 +2891,12 @@ "@types/node": "*" } }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -3015,6 +3022,27 @@ "redux": "^4.0.0" } }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, "node_modules/@types/retry": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", @@ -12339,9 +12367,9 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-redux": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", - "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==", + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", "dependencies": { "@babel/runtime": "^7.15.4", "@types/react-redux": "^7.1.20", @@ -12351,7 +12379,7 @@ "react-is": "^17.0.2" }, "peerDependencies": { - "react": "^16.8.3 || ^17" + "react": "^16.8.3 || ^17 || ^18" }, "peerDependenciesMeta": { "react-dom": { @@ -12597,9 +12625,9 @@ } }, "node_modules/redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "dependencies": { "@babel/runtime": "^7.9.2" } @@ -17339,6 +17367,12 @@ "@types/node": "*" } }, + "@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, "@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -17464,6 +17498,27 @@ "redux": "^4.0.0" } }, + "@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dev": true, + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, "@types/retry": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", @@ -24459,9 +24514,9 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "react-redux": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", - "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==", + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", "requires": { "@babel/runtime": "^7.15.4", "@types/react-redux": "^7.1.20", @@ -24645,9 +24700,9 @@ } }, "redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "requires": { "@babel/runtime": "^7.9.2" } diff --git a/package.json b/package.json index 8f39e1399..27315da91 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@testing-library/jest-dom": "5.15.0", "@testing-library/react": "12.1.2", "@types/jest": "27.0.2", + "@types/react-router-dom": "^5.3.3", "babel-loader": "8.2.3", "babel-plugin-styled-components": "1.13.3", "concurrently": "6.3.0", @@ -43,9 +44,9 @@ "nanoid": "3.1.30", "react": "17.0.2", "react-dom": "17.0.2", - "react-redux": "^7.2.6", + "react-redux": "^7.2.9", "react-router-dom": "5.2.0", - "redux": "^4.2.0", + "redux": "^4.2.1", "sass": "^1.32.8", "style-loader": "^3.3.1", "styled-components": "5.3.3" diff --git a/src/actions/favoritesActions.js b/src/actions/favoritesActions.js index 250c897b3..bd6b3f562 100644 --- a/src/actions/favoritesActions.js +++ b/src/actions/favoritesActions.js @@ -2,14 +2,14 @@ export const TOGGLE_FAVORITES = "TOGGLE_FAVORITES"; export const ADD_FAVORITE = "ADD_FAVORITE"; export const REMOVE_FAVORITE = "REMOVE_FAVORITE"; -export const toggleFavorites = ()=> { - return({type: TOGGLE_FAVORITES}); -} +export const toggleFavorites = () => { + return({ type : TOGGLE_FAVORITES}); +}; export const addFavorite = (movie)=> { return({type: ADD_FAVORITE, payload:movie}); -} +}; export const removeFavorite = (id)=> { return({type: REMOVE_FAVORITE, payload:id}); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/actions/movieActions.js b/src/actions/movieActions.js index 3fb8c9ddb..e595c86b8 100644 --- a/src/actions/movieActions.js +++ b/src/actions/movieActions.js @@ -1,5 +1,11 @@ export const DELETE_MOVIE = "DELETE_MOVIE"; +export const ADD_MOVIE = "ADD_MOVIE" export const deleteMovie = (id)=>{ return({type: DELETE_MOVIE, payload:id}); +} + +export const addMovie = (movie)=> { + // console.log("action: ", movie) + return({type: ADD_MOVIE, payload: movie}) } \ No newline at end of file diff --git a/src/components/AddMovieForm.js b/src/components/AddMovieForm.js index d8a4d768c..5aa848011 100644 --- a/src/components/AddMovieForm.js +++ b/src/components/AddMovieForm.js @@ -6,7 +6,7 @@ import { Link, useHistory } from 'react-router-dom'; const AddMovieForm = (props) => { const { push } = useHistory(); - + const { addMovie } = props const [movie, setMovie] = useState({ title: "", director: "", @@ -22,14 +22,16 @@ const AddMovieForm = (props) => { }); } - const handleSubmit = (e) => { - } + const { title, director, genre, metascore, description } = movie; + + + // console.log("what we are submitting: ", movie) return(
-
+ {e.preventDefault; addMovie(movie); push('/movies') }}>

Add Movie

@@ -67,4 +69,9 @@ const AddMovieForm = (props) => {
); } -export default AddMovieForm; \ No newline at end of file + + +// export default AddMovieForm; + + +export default connect(null, {addMovie})(AddMovieForm) \ No newline at end of file diff --git a/src/components/App.js b/src/components/App.js index d2d276d38..9b5741e2d 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -11,6 +11,7 @@ import MovieHeader from './MovieHeader'; import AddMovieForm from './AddMovieForm'; import FavoriteMovieList from './FavoriteMovieList'; + const App = props => { const displayFavorites = true; diff --git a/src/components/FavoriteMovieList.js b/src/components/FavoriteMovieList.js index 54f4383fa..f05356476 100644 --- a/src/components/FavoriteMovieList.js +++ b/src/components/FavoriteMovieList.js @@ -1,10 +1,14 @@ import React from 'react'; +import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; +import { removeFavorite } from '../actions/favoritesActions'; const FavoriteMovieList = (props) => { - const favorites = []; + // const favorites = []; + const {favorites, removeFavorite } = props; + return (
Favorite Movies
@@ -13,7 +17,7 @@ const FavoriteMovieList = (props) => { return
{movie.title} - remove_circle + {removeFavorite(movie.id)}} class="material-icons">remove_circle
}) @@ -22,4 +26,10 @@ const FavoriteMovieList = (props) => { } -export default FavoriteMovieList; \ No newline at end of file +const mapStateToProps = state =>{ + return({ + favorites: state.favoritesReducer.favorites, + displayFavorites: state.favoritesReducer.displayFavorites + }) +} +export default connect(mapStateToProps, {removeFavorite}) (FavoriteMovieList); \ No newline at end of file diff --git a/src/components/Movie.js b/src/components/Movie.js index 373201e56..56d13c9b1 100644 --- a/src/components/Movie.js +++ b/src/components/Movie.js @@ -1,12 +1,19 @@ import React from 'react'; import { useParams, useHistory } from 'react-router-dom'; +import { connect } from 'react-redux'; +import { deleteMovie } from '../actions/movieActions'; +import { addFavorite } from '../actions/favoritesActions'; + const Movie = (props) => { const { id } = useParams(); const { push } = useHistory(); - const movies = []; - const movie = movies.find(movie=>movie.id===Number(id)); + // const movies = []; + const {movies, deleteMovie, addFavorite} = props; + const movie = movies.find(movie => movie.id===Number(id)); + console.log("movies: ", movies) + return(
@@ -37,8 +44,8 @@ const Movie = (props) => {
- Favorite - + {addFavorite(movie);push('/movies')} } className="m-2 btn btn-dark">Favorite + {deleteMovie(movie.id); push('/movies')}} className="delete">
@@ -47,4 +54,14 @@ const Movie = (props) => {
); } -export default Movie; \ No newline at end of file +// export default Movie; + +const mapStateToProps = (state) => { + return { + movies : state.movieReducer.movies + + + } + } + +export default connect(mapStateToProps, {addFavorite, deleteMovie})(Movie) \ No newline at end of file diff --git a/src/components/MovieHeader.js b/src/components/MovieHeader.js index e3443e5fb..b550ef681 100644 --- a/src/components/MovieHeader.js +++ b/src/components/MovieHeader.js @@ -1,14 +1,16 @@ import React from 'react'; import { Link } from 'react-router-dom'; +import { connect } from 'react-redux'; const MovieHeader = (props) => { const appTitle = ""; - const displayFavorites = true; + // const displayFavorites = true; + const {displayFavorites} = props; return(
-

{appTitle}

+

{props.appTitle}

{ displayFavorites ? "Hide" : "Show"} Favorites
@@ -18,5 +20,13 @@ const MovieHeader = (props) => {
); } +const mapStateToProps = state => { + return { + // movies: state.movies, + appTitle: state.movieReducer.appTitle, + displayFavorites: state.favoritesReducer.displayFavorites + } + } -export default MovieHeader; \ No newline at end of file +export default connect(mapStateToProps, {})(MovieHeader) +// export default MovieHeader; diff --git a/src/components/MovieList.js b/src/components/MovieList.js index 268c78099..d864d9726 100644 --- a/src/components/MovieList.js +++ b/src/components/MovieList.js @@ -2,9 +2,13 @@ import React from 'react'; import MovieListItem from './MovieListItem'; import MovieFooter from './MovieFooter'; +import { connect } from 'react-redux'; + +// * [ ] **The MovieList component prints all of our movies to the screen.** Use the connect method here to map the movies state value into props. Replace our static movie variable with that prop. + const MovieList = (props)=> { - const movies = []; + const {movies} = props; return (
@@ -31,4 +35,12 @@ const MovieList = (props)=> { ); } -export default MovieList; \ No newline at end of file + +const mapStateToProps = state => { + return { + movies: state.movieReducer.movies, + } + } + +export default connect(mapStateToProps, {})(MovieList) +// export default MovieList; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 227c0531b..93e7eee5f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { createStore } from 'redux'; +// import { createStore } from 'redux'; +import { legacy_createStore as createStore } from 'redux' import { Provider } from 'react-redux'; import reducer from './reducers'; @@ -8,10 +9,18 @@ import reducer from './reducers'; import App from './components/App'; import { BrowserRouter as Router } from 'react-router-dom'; import './index.css'; +import favoritesReducer from './reducers/favoritesReducers'; +import rootReducer from './reducers'; + + +const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION && window.__REDUX_DEVTOOLS_EXTENSION()) ReactDOM.render( + - + + + , document.getElementById('root') ); diff --git a/src/reducers/favoritesReducers.js b/src/reducers/favoritesReducers.js new file mode 100644 index 000000000..711cc7504 --- /dev/null +++ b/src/reducers/favoritesReducers.js @@ -0,0 +1,43 @@ +import { ADD_FAVORITE, REMOVE_FAVORITE, TOGGLE_FAVORITES } from '../actions/favoritesActions.js'; +import movies from '../data.js'; + + +const initialState = { + favorites: [], + displayFavorites: true, + movies: movies +} + + +const favoritesReducer = (state = initialState, action) => { + switch(action.type){ + + case (ADD_FAVORITE): { + + return { + ...state, + favorites: [...state.favorites, action.payload] + } + } + + case (TOGGLE_FAVORITES): { + return { + ...state, + displayFavorites: !state.displayFavorites + } + } + + case (REMOVE_FAVORITE): { + return { + ...state, + favorites: state.favorites.filter(item => (item.id !== action.payload)) + } + } + + default: + return state + } + +} + +export default favoritesReducer; \ No newline at end of file diff --git a/src/reducers/index.js b/src/reducers/index.js index 6629609ca..fef9b9b2c 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -1,5 +1,9 @@ import { combineReducers } from 'redux'; import movieReducer from './movieReducer'; +import favoritesReducer from './favoritesReducers'; +const rootReducer = combineReducers({ + movieReducer + ,favoritesReducer}) -export default movieReducer; \ No newline at end of file +export default rootReducer; diff --git a/src/reducers/movieReducer.js b/src/reducers/movieReducer.js index 16f33bcdd..18aaabecf 100644 --- a/src/reducers/movieReducer.js +++ b/src/reducers/movieReducer.js @@ -6,12 +6,23 @@ const initialState = { appTitle: "IMDB Movie Database" } -const reducer = (state, action) => { +const reducer = (state = initialState, action) => { switch(action.type) { case DELETE_MOVIE: return { + ...state, movies: state.movies.filter(item=>(action.payload !== item.id)) } + case ADD_MOVIE: + const newMovie = { + ...action.payload, + id: Date.now() + } + console.log("newMovie: ", newMovie) + return{ + ...state, + movies: [...state.movies, newMovie] + } default: return state; }