I'm in the learning phase so please let me know if something feels illogical. I have been trying to use show result in a way that by default a user should see the trending API results and when he starts searching the other API with search query will trigger or get called. I tried to use if-else but there is no result. It shows the blank. I do not know how can I achieve it. Would really appreciate it if you can help me with this.
when I get the data I am displaying that data in the card below as you will see using the map function, and if the search is blank am trying to show the error. but I am getting a blank card as you can see in the image. how can I also fix this?
Please see the code I have tried. I do not know if it is the logical way or not. Thanks a million in advance.
import React ,{useEffect, useState} from 'react';
import {Grid, Button, Card, CardActionArea, CardActions, CardContent, CardMedia, Typography, makeStyles } from '@material-ui/core';
import Rating from '@material-ui/lab/Rating';
import Watchlist from '../Products/Watchlist.jsx';
import Error from '../Error/Error.jsx';
const useStyles = makeStyles (theme => ({
cardButton:{
'&:hover':{
backgroundColor:'#356E44',
color: 'white',
}
},
Main:{
width: "100vw",
justifyContent: " flex-start",
display: "flex",
flexWrap: "wrap",
alignItems: "stretch",
alignContent: " flex-start",
columnGap: "20px",
rowGap:"5px",
},
cardMain:{
width: 'min-content',
height: 'min-content',
margin: '10px',
'&:hover':{
boxShadow: '5px 3px 5px white',
}
},
cardImage:{
height: '150px',
marginLeft: '7px',
marginRight: '7px',
marginTop: '5px',
objectFit: 'cover',
},
cardContent: {
width: '250px',
height: '50px',
wordBreak: 'break-word',
fontSize: '1rem',
fontWeight: 'bolder',
},
typography1:{
float: 'right',
},
typography2:{
float: 'left',
},
}));
const Produts = (props) =>{
const classes = useStyles();
const [movieData, setMovieData] = useState([]);
const [watchlist, setWatchlist] = useState ([]);
const filterData = ((val) => {
const keyword = props.searchText;
if (keyword !== "") {
return val
}
else if (val.original_title?.toLowerCase().includes(props.searchText?.toLowerCase())) {
return val
}
} );
useEffect (()=> {
const getMovieList = async () => {
const TrendingUrl = 'https://api.themoviedb.org/3/trending/all/day?api_key=API_keyID' ;
const allResultsUrl = `http://api.themoviedb.org/3/search/movie?api_key=API_keyID&query=${props.searchText}`;
const response = await fetch (props.searchText ? TrendingUrl : allResultsUrl);
try {
const responseJson = await response.json();
const data = (responseJson.results);
setMovieData(data);
console.log(data)
} catch (err) {
console.error(err);
}
};
getMovieList();
}, [props.searchText]);
const handleWatchlist = (movieData) => {
const newWatchlist = [...watchlist , movieData];
setWatchlist (newWatchlist);
}
return (
<>
<Grid
item
xs
container
direction="column"
justifyContent="flex-start"
alignItems="flex-start"
style={{ backgroundColor: "black", width: "100%", height: "100%"}}
>
<div className ={classes.Main} > { movieData.length === 0 ? (<h1 style = {{color: 'red', fontSize : "2rem"}}>No search results found</h1>)
: movieData.filter(filterData).map((movie) =>{
return(
<Card className={classes.cardMain} key={movie.id}>
<CardActionArea>
<CardMedia className = {classes.cardImage}>
<img style = {{width: '100%', height: '100%', objectFit: 'cover'}}
src ={`https://image.tmdb.org/t/p/original${movie.poster_path}`}
alt = "movie poster"/>
</CardMedia>
<CardContent className = {classes.cardContent}>
<Typography> {movie.original_title} </Typography>
<Typography
className = {classes.typography1}
variant="body2"
component = "p"
> {movie.release_date}
</Typography>
<Rating
className = {classes.typography2}
name = "ratings"
value = {movie.vote_average/2}
precision={0.5}
readOnly
/>
</CardContent>
</CardActionArea>
<CardActions style = {{justifyContent: 'space-evenly'}} >
<Button className = {classes.cardButton} size = "small">Watch</Button>
<Button className = {classes.cardButton} size = "small" >Share</Button>
<Button className = {classes.cardButton}size = "small" onClick = { () => handleWatchlist(movie) }> Add </Button>
</CardActions>
</Card>
);
})}
</div>
</Grid>
</>
)
};
export default Produts;
CodePudding user response:
I will split the API into two. Also, renamed the variables a bit for a better naming convention.
const fetchTrending = () => {
const url = 'https://api.themoviedb.org/3/trending/all/day?api_key=api_keyID';
fetch(url)
.then(response => response.json())
.then(trendingMovies => setMovies(trendingMovies))
.catch(error => {
// TODO: Do your error handling here
console.error(error);
});
};
const fetchQuery = search => {
const url = `http://api.themoviedb.org/3/search/movie?api_key=api_keyID&query=${search}`;
fetch(url)
.then(response => response.json())
.then(queriedMovies => setMovies(queriedMovies))
.catch(error => {
// TODO: Do your error handling here
console.error(error);
});
};
/* When the component is mounted, load trending movies
This will only run once.
*/
useEffect(() => fetchTrending(), []);
// pass this handler to the onChange listener of your search field
const handleSearchChange = search => fetchQuery(search);
CodePudding user response:
I think this might help you. I'm using this little hook a lot for stuff like this.
It works the same as useMemo, but allows you to pass a callback that return a promise rather than immediately returning a value.
Use it like this:
import { usePromiseMemo } from 'usePromiseMemo';
function MyComponent() {
const movieData = usePromiseMemo(
() =>
fetch(
props.searchText
? `http://api.themoviedb.org/3/search/movie?api_key=api_keyID&query=${props.searchText}`
: `https://api.themoviedb.org/3/trending/all/day?api_key=api_keyID`
)
.then(r => r.json())
.then(r => {
console.log('Raw data: ', r);
const data = r.results;
console.log('Data: ', data);
return data;
})
.catch(err => {
console.log(err);
}),
[props.searchText]
);
return <></>;
}
Also, what do you get from the "Raw data"-log?