I'm trying to make react movie app with api.
I tried to fetch api with query.
This is working only when it matches with certain characters.
For example, if I put 'jur' in searchbar, there is no result, but if I put 'jurassic', and then the results show up.
import { useEffect, useState } from "react"
import Movie from "../components/Movie"
function Home() {
const [loading, setLoading] = useState(true)
const [movies, setMovies] = useState([])
const [searchTerm, setSearchTerm] = useState("")
const getMovies = async () => {
const json = await (
await fetch(
`https://yts.mx/api/v2/list_movies.json?sort_by=year&query_term=${searchTerm}`
)
).json()
setMovies(json.data.movies)
if (movies !== []) setLoading(false)
}
const inputHandler = (e) => {
setSearchTerm(e.target.value.toLowerCase())
}
useEffect(() => {
getMovies()
}, [searchTerm])
return (
<div className="App">
<input
onChange={inputHandler}
value={searchTerm}
placeholder=""
></input>
{loading || movies == null ? (
<h1>Loading...</h1>
) : (
<div>
{searchTerm}
{movies.map((movie) => (
<Movie
key={movie.id}
id={movie.id}
coverImg={movie.medium_cover_image}
title={movie.title}
year={movie.year}
rating={movie.rating}
/>
))}
</div>
)}
</div>
)
}
export default Home
Related
I have the following code:
function App() {
const [countries,setCountries]= useState([]);
const [search, setSearch] = useState('');
//Take data from API with useEffect, async/await and try/catch
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('https://restcountries.com/v2/all');
setCountries(response.data);
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
const filteredCountries = countries.filter((country) =>
country.name.toLowerCase().includes(search.toLowerCase())
);
const handleSelect = (country) => {
setSearch(country.name);
}
return (
<>
<div>
<SearchBar onChange={(e)=> setSearch(e.target.value)} />
{
<ul className="list">
{search.length > 0 && filteredCountries.map((country) => (
<li key={country.name} onClick={() => handleSelect(country)}>
{country.name}
</li>
))}
</ul>
}
</div>
<div className="map-container">
</div>
</>
)
}
export default App;
The result is this:
List image
How can I select an item from the list, e.g. if I search for Ital, Italy appears and I would like to select it and have it appear in the search bar.
I would like to create a search bar to find a country and select it, it should appear in the search bar after being selected.
CodeSandBox Link: https://codesandbox.io/p/github/pierre1590/Population-Tracker/draft/gallant-gagarin?file=%2Fsrc%2Fcomponents%2FMap%2FMap.js
Add value={search} in your <SearchBar/> component.
eg: <SearchBar value={search} onChange={(e)=> setSearch(e.target.value)} />
Below is the full code (I've used a normal input tag in place of your SearchBar component)
import { useState, useEffect } from "react";
import axios from 'axios';
function App() {
const [countries,setCountries]= useState([]);
const [search, setSearch] = useState('');
console.log(search)
//Take data from API with useEffect, async/await and try/catch
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('https://restcountries.com/v2/all');
setCountries(response.data);
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
const filteredCountries = countries.filter((country) =>
country.name.toLowerCase().includes(search.toLowerCase())
);
const handleSelect = (country) => {
setSearch(country.name);
}
return (
<>
<div>
<input value={search} onChange={(e)=> setSearch(e.target.value)} />
{
<ul className="list">
{search.length > 0 && filteredCountries.map((country) => (
<li key={country.name} onClick={() => handleSelect(country)}>
{country.name}
</li>
))}
</ul>
}
</div>
<div className="map-container">
</div>
</>
)
}
export default App;
CodeSandBox Link - https://codesandbox.io/s/enter-the-selected-item-in-the-search-bar-in-react-js-582rez?file=/src/App.js
I have dynamic routes based on search results. How do I go back and see my previously rendered search results & search term in input field versus and empty Search page?
I've started looking into useHistory/useLocation hooks, but I'm lost.
1. Search page
export default function Search() {
const [searchValue, setSearchValue] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [noResults, setNoResults] = useState(false);
const [data, setData] = useState([]);
const fetchData = async () => {
const res = await fetch(
`https://api.themoviedb.org/3/search/movie?api_key={API_KEY}&query=${searchValue}`
);
const data = await res.json();
const results = data.results;
if (results.length === 0) setNoResults(true);
setData(results);
setIsLoading(false);
};
function handleSubmit(e) {
e.preventDefault();
setIsLoading(true);
fetchData();
// setSearchValue("");
}
return (
<div className="wrapper">
<form className="form" onSubmit={handleSubmit}>
<input
placeholder="Search by title, character, or genre"
className="input"
value={searchValue}
onChange={(e) => {
setSearchValue(e.target.value);
}}
/>
</form>
<div className="page">
<h1 className="pageTitle">Explore</h1>
{isLoading ? (
<h1>Loading...</h1>
) : (
<div className="results">
{!noResults ? (
data.map((movie) => (
<Result
poster_path={movie.poster_path}
alt={movie.title}
key={movie.id}
id={movie.id}
title={movie.title}
overview={movie.overview}
release_date={movie.release_date}
genre_ids={movie.genre_ids}
/>
))
) : (
<div>
<h1 className="noResults">
No results found for <em>"{searchValue}"</em>
</h1>
<h1>Please try again.</h1>
</div>
)}
</div>
)}
</div>
</div>
);
}
2. Renders Result components
export default function Result(props) {
const { poster_path: poster, alt, id } = props;
return (
<div className="result">
<Link
to={{
pathname: `/results/${id}`,
state: { ...props },
}}
>
<img
src={
poster
? `https://image.tmdb.org/t/p/original/${poster}`
: "https://www.genius100visions.com/wp-content/uploads/2017/09/placeholder-vertical.jpg"
}
alt={alt}
/>
</Link>
</div>
);
}
3. Clicking a result brings you to a dynamic page for that result.
export default function ResultPage(props) {
const [genreNames, setGenreNames] = useState([]);
const {
poster_path: poster,
overview,
title,
alt,
release_date,
genre_ids: genres,
} = props.location.state;
const date = release_date.substr(0, release_date.indexOf("-"));
useEffect(() => {
const fetchGenres = async () => {
const res = await fetch(
"https://api.themoviedb.org/3/genre/movie/list?api_key={API_KEY}"
);
const data = await res.json();
const apiGenres = data.genres;
const filtered = [];
apiGenres.map((res) => {
if (genres.includes(res.id)) {
filtered.push(res.name);
}
return filtered;
});
setGenreNames(filtered);
};
fetchGenres();
}, [genres]);
return (
<div className="resultPage">
<img
className="posterBackground"
src={
poster
? `https://image.tmdb.org/t/p/original/${poster}`
: "https://www.genius100visions.com/wp-content/uploads/2017/09/placeholder-vertical.jpg"
}
alt={alt}
/>
<div className="resultBackground">
<div className="resultInfo">
<h1> {title} </h1>
</div>
</div>
</div>
);
}
4. How do I go back and see my last search results?
I'm not sure how to implement useHistory/useLocation with dynamic routes. The stuff I find online mentions building a button to click and go to last page, but I don't have a button that has to be clicked. What is someone just swipes back on their trackpad?
One way you could do this would be to persist the local component state to localStorage upon updates, and when the component mounts read out from localStorage to populate/repopulate state.
Use an useEffect hook to persist the data and searchValue to localStorage, when either updates.
useEffect(() => {
localStorage.setItem('searchValue', JSON.stringify(searchValue));
}, [searchValue]);
useEffect(() => {
localStorage.setItem('searchData', JSON.stringify(data));
}, [data]);
Use an initializer function to initialize state when mounting.
const initializeSearchValue = () => {
return JSON.parse(localStorage.getItem('searchValue')) ?? '';
};
const initializeSearchData = () => {
return JSON.parse(localStorage.getItem('searchData')) ?? [];
};
const [searchValue, setSearchValue] = useState(initializeSearchValue());
const [data, setData] = useState(initializeSearchData());
I'm trying to display a list of movies title based on the year the user searches for. I dont need to loop all the pages, the results of the first page is enough. I really don't know what am I doing wrong that when I click search the app crashes.
import React, { useState } from "react";
const Movies = () => {
const [search, setSearch] = useState(false);
const [message, setMessage] = useState(null);
const [year, setYear] = useState("");
const [movies, setMovies] = useState([]);
const searchMovies = async (e) => {
e.preventDefault();
setSearch(true);
const url = `https://jsonmock.hackerrank.com/api/movies?Year=${year}`;
try {
const response = await fetch(url);
const data = await response.json();
setMessage(null);
setMovies(data); // needs check
setSearch(false);
} catch (err) {
setMessage("Unexpected Error happened");
setSearch(false);
}
};
return (
<div>
<form action="" onSubmit={searchMovies}>
<input
type="text"
placeholder="Enter a year"
name="year"
value={year}
onChange={(e) => {
setYear(e.target.value);
}}
/>
<button type="submit">Searc</button>
</form>
<div className="movies-container">
{search && !message ? (
<span>Loading...</span>
) : message ? (
<div className="message"> {message} </div>
) : (
movies.map((movie) => <li key={movie.imdbID}> Title: {movie.Title}</li>)
)}
</div>
</div>
);
};
export default Movies;
You need to make sure movies is an array,
Check the value of movies by adding a console log
const data = await response.json();
console.log("data ", data);
setMovies(data);
And i saw the json of your API think you need to put setMovies(data.data); instead of setMovies(data);
I have a search bar. when I enter the name of any film, filtered data has to be displayed (as a result of fetching from API)
function App() {
const [films, setFilms] = useState([])
const [searchText, setSearchText] = useState('')
const [filteredRes, setFilteredRes] = useState([])
const url = `http://www.omdbapi.com/?apikey=KEY&s=${searchText}`
useEffect (()=> {
loadData()
}, [searchText])
const loadData = async () => {
const res = await axios.get(url)
setFilms(res.data.Search)
const filtered = await films.filter(i => i.toLowerCase().includes(searchText))
setFilteredRes(filtered)
}
const onTextChange = (e) => {
setSearchText(e.target.value)
}
return (
<>
<Container>
<h1>Bookstore</h1>
<Row>
<form>
<input type='text'
placeholder='Search...'
name="searchText"
onChange={onTextChange}
value={searchText}
/>
</form>
</Row>
<Row>
{ filteredRes.map(item => {
return (
<Col lg={3} md={3} sm={12} key={item.imdbID}>
<img src={item.poster}/>
<h6>{item.title}</h6>
<h6>{item.year}</h6>
</Col>
)
})}
</Row>
</Container>
</>
);
}
I have am issue:
Unhandled Rejection (TypeError): Cannot read property 'filter' of undefined. But why if I save fetched films in const [ films ] ?
Ooutput:
You don't have to filter fetched data additionally, the data that is being fetched by Axios is already filtered according to the serchText so Axios call does the all.
Full Example:
import React, { useState } from "react";
import Axios from "axios";
const APIKEY = "your api";
export default function App() {
const [searchText, setSearchText] = useState("");
const [films, setFilms] = useState([]);
const handleSubmit = (event) => {
event.preventDefault();
if (!searchText) {
alert("Please enter movie name");
return;
}
Axios.get(
`http://www.omdbapi.com/?i=tt3896198&apikey=${APIKEY}&s=${searchText}`
)
.then((response) => {
console.log(response.data);
setFilms(response.data.Search);
})
.catch((error) => {
console.log(error);
});
};
return (
<div className="App">
<form onSubmit={handleSubmit}>
<input
placeholder={"Search movie"}
onChange={(event) => {
setSearchText(event.target.value);
}}
/>
<button>Search</button>
</form>
{films.map((film) => (
<div>
<img src={film["Poster"]} alt={film["Title"]} width={100} />
<p>{film["Title"]}</p>
<p>{film["Year"]}</p>
</div>
))}
</div>
);
}
Github Repo
Using react hooks, I'm making a call to an api and displaying items in the app component calling a book and pagination functional component.
I have a search component placed at the top in the App return. Can anyone please help:
When the search button is clicked after inserting a book name, then books with similar names should be displayed
const SearchBooks =() => {
return (
<InputGroup>
<FormControl
type="text"
placeholder="Search books"
onChange={e => (e.target.value)}
/>
<InputGroup.Append>
<Button >
Search
</Button>
</InputGroup.Append>
</InputGroup>
);
}
const Book = ({books, loading}) => {
if(loading) {
return <h2>Loading...</h2>
}
return (books.map((book) =>
<ListGroup className="text-primary" key={book.id}>
<ListGroup.Item>
<h4>{book.book_title}</h4>
<li>Author : {book.book_author}</li>
<li>Publication Year : {book.book_publication_year}</li>
<li>Publication Country : {book.book_publication_country}</li>
<li>Publication City : {book.book_publication_city}</li>
<li >Pages : {book.book_pages}</li>
</ListGroup.Item>
</ListGroup>
));
}
const App = () => {
const [books, setBooks] = useState([]);
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [booksPerPage] = useState(2);
const [search, setSearch] = useState('');
useEffect(() => {
const fetchBooks = async () => {
setLoading(true);
const res = await axios.post("http://nyx.vima.ekt.gr:3000/api/books");
setBooks(res.data.books);
setLoading(false);
};
fetchBooks();
}, []);
// Get current books
const indexOfLastBook = currentPage * booksPerPage;
const indexOfFirstBook = indexOfLastBook - booksPerPage;
const currentPosts = books.slice(indexOfFirstBook, indexOfLastBook);
// Change page
const paginate = pageNumber => setCurrentPage(pageNumber);
return (
<div className='container mt-5'>
<SearchBook/>
<Book books={currentPosts} loading={loading}/>
<Pagination
booksPerPage={booksPerPage}
totalBooks={books.length}
paginate={paginate}
/>
</div>
);
}
import React from 'react';
const Pagination = ({ booksPerPage, totalBooks, paginate }) => {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalBooks / booksPerPage); i++) {
pageNumbers.push(i);
}
return (
<nav className="justify-content-center">
<ul className='pagination'>
{pageNumbers.map(number => (
<li key={number} className='page-item'>
<a onClick={() => paginate(number)} href='!#' className='page-link'>
{number}
</a>
</li>
))}
</ul>
</nav>
);
};
const currentPosts = books.fliter(book => book.title.includes(keyword)).slice(indexOfFirstBook, indexOfLastBook);
and you should recalculate the pagination and reset page number too, so ppl can still navigate to pages if the search result is too long.
you can also use useMemo hooks to optimize it, so it wont filter the array again on every re-render.
const currentPosts = useMemo(() => books.filter(...).slice(...), [books, keyword]);