From b41004471cffa8ff6925e5ccd4ff79c1999fa469 Mon Sep 17 00:00:00 2001 From: Boris Alexander Hernandez Date: Mon, 10 Jun 2024 19:20:59 -0700 Subject: [PATCH 01/13] feat: Movie information TLDR: Add movie info from API Create elements with React and make GET request to IMDB API. Returns movies now showing in theaters. --- .gitignore | 1 + src/App.css | 5 ++++- src/App.jsx | 11 +++++++++-- src/MovieCard.css | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/MovieCard.jsx | 16 ++++++++++++++++ src/MovieList.css | 11 +++++++++++ src/MovieList.jsx | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/index.css | 2 +- 8 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 src/MovieCard.css create mode 100644 src/MovieCard.jsx create mode 100644 src/MovieList.css create mode 100644 src/MovieList.jsx diff --git a/.gitignore b/.gitignore index a547bf36..7ceb59f8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ dist-ssr *.njsproj *.sln *.sw? +.env diff --git a/src/App.css b/src/App.css index 0bf65669..cff5445a 100644 --- a/src/App.css +++ b/src/App.css @@ -7,10 +7,13 @@ display: flex; flex-direction: row; align-items: center; - justify-content: space-evenly; + justify-content: center; color: white; padding: 20px; } +.App-header h1 { + font-size: 50px; +} @media (max-width: 600px) { .movie-card { diff --git a/src/App.jsx b/src/App.jsx index 48215b3f..e51e6b9e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,17 @@ -import { useState } from 'react' import './App.css' +import MovieList from "./MovieList" +import "../public/movie.png" const App = () => { + return (
- +
+

Flixster

+ +
+
+ ); } export default App diff --git a/src/MovieCard.css b/src/MovieCard.css new file mode 100644 index 00000000..bee67662 --- /dev/null +++ b/src/MovieCard.css @@ -0,0 +1,45 @@ +.card { + display: flex; + flex-direction: column; + width: 200px; + height: 375px; + border: solid black; + background-color: white; + margin: 10px; + box-shadow: 12px 12px 2px 1px rgba(0, 0, 0, 0.582); + position: relative; + top: 0; + transition: top ease 0.5s; +} +.card:hover { + top: -15px; + cursor: pointer; +} + +.img-container { + width: 100%; + height: 80%; + background-color: #ccc; + overflow: contain; +} + +.img-container img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.txt-container { + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 20%; + +} + +.txt-container h2, .txt-container p { + font-size: 15px; + margin: 0%; +} diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx new file mode 100644 index 00000000..7240c7df --- /dev/null +++ b/src/MovieCard.jsx @@ -0,0 +1,16 @@ +import "./MovieCard.css"; +const MovieCard = ({key, poster, title, rating}) => { + + return(//using info from MovieList to create element +
+
+ Movie Poster +
+
+

{title}

+

{rating}

+
+
+ ); +} +export default MovieCard; \ No newline at end of file diff --git a/src/MovieList.css b/src/MovieList.css new file mode 100644 index 00000000..6d2a989c --- /dev/null +++ b/src/MovieList.css @@ -0,0 +1,11 @@ +.movie-row { + display: flex; + flex-wrap: wrap; + /* gap: 10px; Adds space between cards */ + justify-content: space-between; /* Center cards horizontally */ + padding: 20px; + width: 75%; + margin: 0 auto; + + +} \ No newline at end of file diff --git a/src/MovieList.jsx b/src/MovieList.jsx new file mode 100644 index 00000000..0be85fde --- /dev/null +++ b/src/MovieList.jsx @@ -0,0 +1,45 @@ +import { useState, useEffect } from "react"; +import MovieCard from "./MovieCard"; //contains movie information +import "./MovieList.css" + +// Purpose: Create card elements from API info +const MovieList = () => { + const [data,setData] = useState([]) // create data (will hold json elements) + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${import.meta.env.VITE_TOKEN}` //private token used to access api + } + }; + + const fetchData = async () => { + const resp = await fetch('https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=1', options) + + const Data = await resp.json() + + setData(Data.results) + } + useEffect(() => { + fetchData(); + }, []); + + console.log(data) + return(//creates movie card element +
+ {data.length > 0 ? ( + data.map(movie => ( + + )) + ) : ( +

Loading movies...

//error handle if data length == 0 + )} +
+ ); + }; +export default MovieList; \ No newline at end of file diff --git a/src/index.css b/src/index.css index e1faed1a..bb674545 100644 --- a/src/index.css +++ b/src/index.css @@ -1,7 +1,7 @@ body { margin: 0; font-family: Arial, sans-serif; - background-color: #f4f4f4; + background-color: white; } button { From 7a935185ed1cdf2bf9167ee0568d584d89f9bba5 Mon Sep 17 00:00:00 2001 From: Boris Alexander Hernandez Date: Mon, 10 Jun 2024 22:31:13 -0700 Subject: [PATCH 02/13] feat: Add addistional movies tldr: add more info from additional api request Added button at bottom of page that when pressed loads in 20 more movies. This is done by updating the page in fetch to api. it is able to load in as many times as is pressed. --- src/App.css | 17 ++++++++++++++--- src/App.jsx | 8 ++++++-- src/MovieList.css | 7 ++++++- src/MovieList.jsx | 11 ++++++++--- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/App.css b/src/App.css index cff5445a..891b3b1d 100644 --- a/src/App.css +++ b/src/App.css @@ -5,16 +5,27 @@ .App-header { background-color: #282c34; display: flex; - flex-direction: row; + flex-direction: column; align-items: center; justify-content: center; color: white; - padding: 20px; + } -.App-header h1 { + +.title { + display: flex; + align-items: center; + height: 100px; +} + +.title h1 { font-size: 50px; } +.title img { + height: 60px; +} + @media (max-width: 600px) { .movie-card { width: 100%; diff --git a/src/App.jsx b/src/App.jsx index e51e6b9e..13ad602c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,15 +1,19 @@ import './App.css' import MovieList from "./MovieList" +// import Search from './Search' import "../public/movie.png" const App = () => { return (
-

Flixster

- +
+

Flixster

+ +
+
); } diff --git a/src/MovieList.css b/src/MovieList.css index 6d2a989c..531ae92b 100644 --- a/src/MovieList.css +++ b/src/MovieList.css @@ -1,11 +1,16 @@ .movie-row { display: flex; flex-wrap: wrap; - /* gap: 10px; Adds space between cards */ justify-content: space-between; /* Center cards horizontally */ padding: 20px; width: 75%; margin: 0 auto; +} + +.load-btn { + width: 100%; + float: left; + margin-top: 30px; } \ No newline at end of file diff --git a/src/MovieList.jsx b/src/MovieList.jsx index 0be85fde..89a09638 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -5,6 +5,8 @@ import "./MovieList.css" // Purpose: Create card elements from API info const MovieList = () => { const [data,setData] = useState([]) // create data (will hold json elements) + const [page, updatePage] = useState(1);//State starts at 1 since link starts at 1 + const options = { method: 'GET', headers: { @@ -14,15 +16,15 @@ const MovieList = () => { }; const fetchData = async () => { - const resp = await fetch('https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=1', options) + const resp = await fetch(`https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${page}`, options)//fills link const Data = await resp.json() - setData(Data.results) + setData(prevData => [...prevData, ...Data.results]);//appends results to end } useEffect(() => { fetchData(); - }, []); + }, [page]);//adds to update for every new page console.log(data) return(//creates movie card element @@ -39,6 +41,9 @@ const MovieList = () => { ) : (

Loading movies...

//error handle if data length == 0 )} + ); }; From 014ac3088a03b55eec99806c4ecaf217529b9d42 Mon Sep 17 00:00:00 2001 From: Boris Alexander Hernandez Date: Tue, 11 Jun 2024 13:40:33 -0700 Subject: [PATCH 03/13] Add search functionality --- src/App.jsx | 3 +- src/MovieList.jsx | 131 +++++++++++++++++++++++++++++++--------------- 2 files changed, 90 insertions(+), 44 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 13ad602c..575d82a7 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,5 @@ import './App.css' import MovieList from "./MovieList" -// import Search from './Search' import "../public/movie.png" const App = () => { @@ -11,9 +10,9 @@ const App = () => {

Flixster

+ - ); } diff --git a/src/MovieList.jsx b/src/MovieList.jsx index 89a09638..d0a70dfb 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -1,50 +1,97 @@ import { useState, useEffect } from "react"; -import MovieCard from "./MovieCard"; //contains movie information -import "./MovieList.css" +import MovieCard from "./MovieCard"; +import "./MovieList.css"; // Purpose: Create card elements from API info const MovieList = () => { - const [data,setData] = useState([]) // create data (will hold json elements) - const [page, updatePage] = useState(1);//State starts at 1 since link starts at 1 + const [data, setData] = useState([]); + const [page, updatePage] = useState(1); + const [searchText, updateSearchText] = useState(""); - const options = { - method: 'GET', - headers: { - accept: 'application/json', - Authorization: `Bearer ${import.meta.env.VITE_TOKEN}` //private token used to access api - } - }; - - const fetchData = async () => { - const resp = await fetch(`https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${page}`, options)//fills link + const searchMovies = () => { + updatePage(1); + setData([]); + fetchData(); + }; - const Data = await resp.json() - - setData(prevData => [...prevData, ...Data.results]);//appends results to end - } - useEffect(() => { + const resetPage = () => { + console.log("resetPage"); + updatePage( + // make sure that fetch data runs after updating prevPage + (prevPage) => prevPage + 1, + () => { fetchData(); - }, [page]);//adds to update for every new page + } + ); + }; + + const options = { + method: "GET", + headers: { + accept: "application/json", + Authorization: `Bearer ${import.meta.env.VITE_TOKEN}`, //private token used to access api + }, + }; + + const fetchData = async () => { + if (searchText === "") { + const resp = await fetch( + `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${page}`, + options + ); //fills link + const Data = await resp.json(); + if (page === 1) { + setData([...Data.results]); + } else { + setData((prevData) => [...prevData, ...Data.results]); //appends results to end + } + console.log("prev", Data.results); + } else { + const resp = await fetch( + `https://api.themoviedb.org/3/search/movie?query=${searchText}&include_adult=false&language=en-US&page=${page}`, + options + ); + const Data = await resp.json(); + if (page === 1) { + setData([...Data.results]); + } else { + setData((prevData) => [...prevData, ...Data.results]); //appends results to end + } + } + }; + useEffect(() => { + fetchData(); + }, [page]); //adds to update for every new page - console.log(data) - return(//creates movie card element -
- {data.length > 0 ? ( - data.map(movie => ( - - )) - ) : ( -

Loading movies...

//error handle if data length == 0 - )} - -
- ); - }; -export default MovieList; \ No newline at end of file + return ( + //creates movie card element + <> +
+ updateSearchText(e.target.value)} + value={searchText} + /> + +
+
+ {data.length > 0 ? ( + data.map((movie) => ( + + )) + ) : ( +

Loading movies...

//error handle if data length == 0 + )} + +
+ + ); +}; +export default MovieList; From c16b8400bc055a1fe715aa762f956de62157e1d0 Mon Sep 17 00:00:00 2001 From: Boris Alexander Hernandez Date: Wed, 12 Jun 2024 13:37:20 -0700 Subject: [PATCH 04/13] feat: Add Modal things I added- 1. Creating modal element that covers screen 2. input element from API 3. create on click events for modal --- src/App.jsx | 27 +++++++------- src/Modal.css | 45 ++++++++++++++++++++++++ src/Modal.jsx | 27 ++++++++++++++ src/MovieCard.jsx | 30 ++++++++-------- src/MovieList.css | 6 ++-- src/MovieList.jsx | 89 ++++++++++++++++++++++++++++++++++++----------- src/main.jsx | 14 ++++---- 7 files changed, 178 insertions(+), 60 deletions(-) create mode 100644 src/Modal.css create mode 100644 src/Modal.jsx diff --git a/src/App.jsx b/src/App.jsx index 575d82a7..201d3bb1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,20 +1,19 @@ -import './App.css' -import MovieList from "./MovieList" -import "../public/movie.png" +import "./App.css"; +import MovieList from "./MovieList"; +import "../public/movie.png"; const App = () => { return ( -
-
-
-

Flixster

- -
- +
+
+
+

Flixster

+ +
- -
+ +
); -} +}; -export default App +export default App; diff --git a/src/Modal.css b/src/Modal.css new file mode 100644 index 00000000..1d996079 --- /dev/null +++ b/src/Modal.css @@ -0,0 +1,45 @@ + +.modal-overlay { + box-sizing: border-box; + position:fixed; + z-index: 1; + left: 0; + top: 0; + bottom: 0; + right: 0; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.4); + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; +} + +.modal-content { + display: flex; + flex-direction: column; + align-items: center; + background-color: #fefefe; + margin: 15% auto; + padding: 20px; + border: 1px solid #888; + width: 40%; + height: 60%; + } + + +.close-modal { + display: flex; + flex-shrink: 0; + font-size: 25px; + cursor: pointer; + margin-left: auto; + justify-content: flex-end; + + } + + .modal-content img { + width: 150px; + + } \ No newline at end of file diff --git a/src/Modal.jsx b/src/Modal.jsx new file mode 100644 index 00000000..5dab3e64 --- /dev/null +++ b/src/Modal.jsx @@ -0,0 +1,27 @@ +import "./Modal.css"; +const Modal = ({ + isOpen, + onClose, + title, + poster, + release, + overview, + genres, +}) => { + const modalStyle = { display: isOpen ? "flex" : "none" }; + return ( +
+
+ + × + +

{title}

+ movie-poster +

Release date: {release}

+

Overview: {overview}

+

Genres: {genres}

+
+
+ ); +}; +export default Modal; diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx index 7240c7df..5f5abb7b 100644 --- a/src/MovieCard.jsx +++ b/src/MovieCard.jsx @@ -1,16 +1,16 @@ import "./MovieCard.css"; -const MovieCard = ({key, poster, title, rating}) => { - - return(//using info from MovieList to create element -
-
- Movie Poster -
-
-

{title}

-

{rating}

-
-
- ); -} -export default MovieCard; \ No newline at end of file +const MovieCard = ({ key, poster, title, rating, clickHandler }) => { + return ( + //using info from MovieList to create element +
+
+ Movie Poster +
+
+

{title}

+

{rating}

+
+
+ ); +}; +export default MovieCard; diff --git a/src/MovieList.css b/src/MovieList.css index 531ae92b..4f75a516 100644 --- a/src/MovieList.css +++ b/src/MovieList.css @@ -1,12 +1,12 @@ .movie-row { display: flex; flex-wrap: wrap; - justify-content: space-between; /* Center cards horizontally */ + justify-content: flex-start; /* Center cards horizontally */ padding: 20px; width: 75%; margin: 0 auto; - - + gap: 2%; + z-index: 0; } .load-btn { diff --git a/src/MovieList.jsx b/src/MovieList.jsx index d0a70dfb..0d11dc2b 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -1,12 +1,23 @@ import { useState, useEffect } from "react"; -import MovieCard from "./MovieCard"; import "./MovieList.css"; +import MovieCard from "./MovieCard"; +import Modal from "./Modal"; // Purpose: Create card elements from API info -const MovieList = () => { +export const MovieList = () => { const [data, setData] = useState([]); const [page, updatePage] = useState(1); const [searchText, updateSearchText] = useState(""); + const [modalOpen, setShowModal] = useState(false); + const [selectedMovieInfo, updateMovieInfo] = useState(null); + + const options = { + method: "GET", + headers: { + accept: "application/json", + Authorization: `Bearer ${import.meta.env.VITE_TOKEN}`, //private token used to access api + }, + }; const searchMovies = () => { updatePage(1); @@ -14,10 +25,8 @@ const MovieList = () => { fetchData(); }; - const resetPage = () => { - console.log("resetPage"); + const changePage = () => { updatePage( - // make sure that fetch data runs after updating prevPage (prevPage) => prevPage + 1, () => { fetchData(); @@ -25,37 +34,52 @@ const MovieList = () => { ); }; - const options = { - method: "GET", - headers: { - accept: "application/json", - Authorization: `Bearer ${import.meta.env.VITE_TOKEN}`, //private token used to access api - }, + const toggleModal = async (movie_id) => { + const movieInfoResponse = await fetch( + `https://api.themoviedb.org/3/movie/${movie_id}?language=en-US`, + options + ); + const movieInfoData = await movieInfoResponse.json(); + updateMovieInfo(movieInfoData); + setShowModal((prev) => !prev); + }; + + const genre_convert = (ids) => { + if (!selectedMovieInfo || !selectedMovieInfo.genres || !ids) { + return ""; + } + return ids + .map( + (id) => selectedMovieInfo.genres.find((genre) => genre.id === id)?.name + ) + .filter((name) => name) // filter out any undefined values + .join(", "); }; const fetchData = async () => { if (searchText === "") { + //add if dropdown menu const resp = await fetch( `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${page}`, options - ); //fills link - const Data = await resp.json(); + ); + const Movies = await resp.json(); if (page === 1) { - setData([...Data.results]); + setData([...Movies.results]); } else { - setData((prevData) => [...prevData, ...Data.results]); //appends results to end + setData((prevData) => [...prevData, ...Movies.results]); //appends results to end } - console.log("prev", Data.results); } else { + //option for drop down const resp = await fetch( `https://api.themoviedb.org/3/search/movie?query=${searchText}&include_adult=false&language=en-US&page=${page}`, options ); - const Data = await resp.json(); + const Movies = await resp.json(); if (page === 1) { - setData([...Data.results]); + setData([...Movies.results]); } else { - setData((prevData) => [...prevData, ...Data.results]); //appends results to end + setData((prevData) => [...prevData, ...Movies.results]); //appends results to end } } }; @@ -73,6 +97,15 @@ const MovieList = () => { value={searchText} /> + +
{data.length > 0 ? ( @@ -82,15 +115,29 @@ const MovieList = () => { title={movie.title} poster={`https://image.tmdb.org/t/p/original${movie.poster_path}`} rating={Math.round(movie.vote_average * 100) / 100} //get two decimal points + clickHandler={() => toggleModal(movie.id)} /> )) ) : ( -

Loading movies...

//error handle if data length == 0 +

No movies found...

//error handle if data length == 0 )} -
+ {modalOpen && selectedMovieInfo && ( + setShowModal(false)} + title={selectedMovieInfo.name} + poster={`https://image.tmdb.org/t/p/original${selectedMovieInfo.poster_path}`} + release={selectedMovieInfo.release_date} + overview={selectedMovieInfo.overview} + genres={genre_convert( + selectedMovieInfo.genres.map((genre) => genre.id) + )} + /> + )} ); }; diff --git a/src/main.jsx b/src/main.jsx index 54b39dd1..b91620d3 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,10 +1,10 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.jsx' -import './index.css' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.jsx"; +import "./index.css"; -ReactDOM.createRoot(document.getElementById('root')).render( +ReactDOM.createRoot(document.getElementById("root")).render( - , -) + +); From f233af4a4b3ceab34e6ab2f88fc8e5b53490e711 Mon Sep 17 00:00:00 2001 From: Boris Alexander Hernandez Date: Thu, 13 Jun 2024 10:21:18 -0700 Subject: [PATCH 05/13] Edit where nav is stored and add sort and filter functionality --- src/MovieList.jsx | 135 +++++++++++++++++++++++++--------------------- src/Nav.css | 26 +++++++++ src/Nav.jsx | 97 +++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 60 deletions(-) create mode 100644 src/Nav.css create mode 100644 src/Nav.jsx diff --git a/src/MovieList.jsx b/src/MovieList.jsx index 0d11dc2b..f0f17b92 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -2,6 +2,7 @@ import { useState, useEffect } from "react"; import "./MovieList.css"; import MovieCard from "./MovieCard"; import Modal from "./Modal"; +import Nav from "./Nav"; // Purpose: Create card elements from API info export const MovieList = () => { @@ -10,6 +11,10 @@ export const MovieList = () => { const [searchText, updateSearchText] = useState(""); const [modalOpen, setShowModal] = useState(false); const [selectedMovieInfo, updateMovieInfo] = useState(null); + const [sortString, sortBy] = useState(""); + const [lastAction, updateLastAction] = useState(""); + const [showSearch, setShowSearch] = useState(false); + const [genre, updateGenreFilter] = useState(""); const options = { method: "GET", @@ -19,10 +24,20 @@ export const MovieList = () => { }, }; - const searchMovies = () => { - updatePage(1); - setData([]); - fetchData(); + const applySort = (newSortString) => { + updateLastAction("sort"); + sortBy(newSortString); // Update sort state + updatePage(1); // Reset to the first page + updateGenreFilter(""); // Clear the genre filter + }; + + const performSearch = () => { + if (searchText === "") { + return; + } + updateLastAction("search"); + updatePage(1); // Reset to the first page + fetchData(); // Manually trigger data fetching }; const changePage = () => { @@ -56,74 +71,74 @@ export const MovieList = () => { .join(", "); }; + useEffect(() => { + fetchData(); + }, [page, sortString, lastAction, genre]); // update on change + const fetchData = async () => { - if (searchText === "") { - //add if dropdown menu - const resp = await fetch( - `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${page}`, - options - ); - const Movies = await resp.json(); - if (page === 1) { - setData([...Movies.results]); - } else { - setData((prevData) => [...prevData, ...Movies.results]); //appends results to end - } - } else { - //option for drop down - const resp = await fetch( - `https://api.themoviedb.org/3/search/movie?query=${searchText}&include_adult=false&language=en-US&page=${page}`, - options - ); - const Movies = await resp.json(); - if (page === 1) { - setData([...Movies.results]); - } else { - setData((prevData) => [...prevData, ...Movies.results]); //appends results to end + let baseUrl = "https://api.themoviedb.org/3"; + let url = `${baseUrl}/movie/now_playing?language=en-US&page=${page}`; + + if (genre) { + url = `${baseUrl}/discover/movie?include_adult=false&include_video=false&language=en-US&page=${page}&with_genres=${genre}`; + } else if (lastAction === "search" && searchText !== "") { + url = `${baseUrl}/search/movie?query=${searchText}&include_adult=false&language=en-US&page=${page}`; + } else if (lastAction === "sort" && sortString !== "") { + url = `${baseUrl}/discover/movie?include_adult=false&include_video=false&language=en-US&page=${page}&sort_by=${sortString}`; + } + + try { + const response = await fetch(url, options); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); } + const data = await response.json(); + setData(page === 1 ? [...data.results] : [...data, ...data.results]); // Handle pagination + } catch (error) { + console.error("Fetching data failed:", error); } }; - useEffect(() => { - fetchData(); - }, [page]); //adds to update for every new page - return ( //creates movie card element <> -
- updateSearchText(e.target.value)} - value={searchText} - /> - - - -
+