i am using TMDB api to make a movie and series website and i tried to use pagination to show more movies in one session and got this error:
'.map is not a function'
if I take the map it receives the information from the api on the console but I can't show it on the screen without the map
here is the code:
import ReactPaginate from 'react-paginate';
const imageUrl = import.meta.env.VITE_URL_BACKGROUND;
import '../components/components-home/PaginationCss.css';
const moviesURL = import.meta.env.VITE_API;
const apiKey = import.meta.env.VITE_API_KEY;
const Home = () => {
const [items, setItems] = useState([]);
useEffect(() => {
const getComments = async () => {
const res = await fetch(`${moviesURL}popular?${apiKey}&page=1`);
const data = await res.json();
setItems(data.results);
};
getComments();
}, []);
console.log(items);
const fetchComments = async (currentPage) => {
const res = await fetch(
`${moviesURL}popular?${apiKey}&page=${currentPage}`
);
const data = await res.json();
return data;
};
const handlePageClick = async (data) => {
console.log(data.selected);
let currentPage = data.selected + 1;
const commentsFormServer = await fetchComments(currentPage);
setItems(commentsFormServer);
};
return (
<div className=''>
<div className=''>
{items.map((item) => {
return (
<div className=''>
<div className=''>
<div className=''>
<img src={imageUrl + item.poster_path} alt='' />
</div>
</div>
</div>
);
})}
</div>
<ReactPaginate
previousLabel={'<<'}
nextLabel={'>>'}
breakLabel={'...'}
pageCount={15}
marginPagesDisplayed={3}
pageRangeDisplayed={6}
onPageChange={handlePageClick}
containerClassName={'pagination'}
activeClassName={'active'}
/>
</div>
);
};
export default Home;
Related
i'm working with React-App and backend/frontend API i get the response everything works fine , but i get too many responses ( over 100 ) at once, how can i go about only getting lets say ( 10 ) at a time, i've tried many things but they dont work. this is my code.
NOT ASKING FOR SOMEONE TO DO THE CODE FOR ME, BUT FOR A LITTLE HELP PUTTING ME ON THE RIGHT DIRECTION
REST API
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react'
const baseUrl = 'http://localhost:3008';
const createRequest = (url) => ({ url });
export const playersAPI = createApi({
reducerPath: 'playersAPI',
baseQuery: fetchBaseQuery({ baseUrl }),
endpoints: (builder) => ({
getplayersAPI: builder.query({
query: (count) => createRequest(`/api/players?limit=${count}`),
}),
})
});
export const { useGetplayersAPIQuery } = playersAPI;
Front Page
import React, { useEffect, useState } from 'react';
import millify from 'millify';
import { Typography, Row, Col, Statistic } from 'antd';
import { Link } from 'react-router-dom';
import { Card } from 'antd';
import { useGetplayersAPIQuery } from '../services/playersAPI';
const { Title } = Typography;
const Players = ({ simplified }) => {
const count = simplified ? 10 : 100;
const { data: playersList, isFetching } = useGetplayersAPIQuery(count);
const [players, setPlayers] = useState();
const [searchTerm, setSearchTerm] = useState('');
console.log(players)
useEffect(() => {
setPlayers(playersList?.players);
const filteredData = playersList?.players.filter((name) => name.name.toLowerCase().includes(searchTerm));
setPlayers(filteredData);
}, [playersList, searchTerm]);
if (isFetching) return 'Loading...';
return (
<>
<div className="search-crypto">
<input placeholder="Search Players" onChange={(e) => setSearchTerm(e.target.value)} />
</div>
<Row gutter={[15, 15]} className="crypto-card-container">
{players?.map((name) => (
<Col xs={24} sm={12} lg={6} className="crypto-card" key={name.id}>
<Link to={`/players/${name.id}`}>
<Card
title={`${name.name}`}
hoverable
>
<p>Name: {(name.name)}</p>
<p>Status: {(name.status)}</p>
<p>Alliancce: {(name.alliance)}</p>
</Card>
</Link>
</Col>
))}
</Row>
</>
)
}
export default Players
This is my front page the squares the ones i want to show only 10 of them at a time, right now it shows all the data from the API.
[https://i.stack.imgur.com/UKHLi.jpg]
If you can't change your backend to add an extra parameter offset, you can do something like this.
const PlayersScreen = () => {
const [data, setData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [playersPerPage] = useState(10);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchPlayers = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await res.json();
setData(data);
setLoading(false)
};
fetchPlayers();
}, []);
// Get current players
const indexOfLastPlayer = currentPage * playersPerPage;
const indexOfFirstPlayer = indexOfLastPlayer - playersPerPage;
const currentPlayers = data.slice(indexOfFirstPlayer, indexOfLastPlayer);
// Change page
const paginate = pageNumber => setCurrentPage(pageNumber);
return (
<div className='container mt-5'>
<h1 className='text-primary mb-3'>My Players</h1>
<Players players={currentPlayers} loading={loading} />
<Pagination
playersPerPage={playersPerPage}
totalPlayers={data.length}
paginate={paginate}
/>
</div>
);
}
const Players = ({ players, loading }) => {
if (loading) {
return <h2>Loading...</h2>;
}
return (
<ul className='list-group mb-4'>
{players.map(player => (
<li key={player.id} className='list-group-item'>
{player.title}
</li>
))}
</ul>
);
}
const Pagination = ({ playersPerPage, totalPlayers, paginate }) => {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalPlayers / playersPerPage); i++) {
pageNumbers.push(i);
}
return (
<nav>
<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>
);
}
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 am trying to organize my code order to handle feed as feed.* based on my endpoint API, but however react doesn't allow me to directly send functions into component, but I want something similar to feed.results, feed. count
const [initialized, setIntialized] = useState(false);
const [feed, setFeed] = useState([]);
const browserFeed = async () => {
const response = await browse();
setFeed(response.results);
setIntialized(true);
};
useEffect(() => {
if (!initialized) {
browserFeed();
}
});
export const browse = () => {
return api.get('xxxxxxxx')
.then(function(response){
return response.data // returns .count , .next, .previous, and .results
})
.catch(function(error){
console.log(error);
});
}
<div className="searched-jobs">
<div className="searched-bar">
<div className="searched-show">Showing {feed.count}</div>
<div className="searched-sort">Sort by: <span className="post-time">Newest Post </span><span className="menu-icon">▼</span></div>
</div>
<div className="job-overview">
<div className="job-overview-cards">
<FeedsList feeds={feed} />
<div class="job-card-buttons">
<button class="search-buttons card-buttons-msg">Back</button>
<button class="search-buttons card-buttons">Next</button>
</div>
</div>
</div>
</div>
If it is pagination you are trying to handle here is one solution:
async function fetchFeed(page) {
return api.get(`https://example.com/feed?page=${page}`);
}
const MyComponent = () => {
const [currentPage, setCurrentPage] = useState(1);
const [feed, setFeed] = useState([]);
// Fetch on first render
useEffect(() => {
fetchFeed(1).then((data) => setFeed(data));
}, []);
// Update feed if the user changes the page
useEffect(() => {
fetchFeed(currentPage).then((data) => setFeed(data));
}, [currentPage]);
const isFirstPage = currentPage === 1;
return (
<>
<FeedsList feeds={feed} />
{isFirstPage && (
<button onClick={() => setCurrentPage(currentPage - 1)}>Back</button>
)}
<button Click={() => setCurrentPage(currentPage + 1)}>Next</button>
</>
);
};
After submitting with UpdateParams, the new url is called and a JSON object with the new queried data is returned as expected.
The form updates the two state vars.
However, the products in the all-products view are not updated to reflect the form input.
What do I need to do to to refresh the render to reflect the new data in product?
//all-products.js
import Link from 'next/link'
import React from 'react';
import { useState } from 'react';
//gets data from local api
async function getData(rank, keyword){
const res = await fetch(`http://localhost:4000/api?top=${rank}&keyword=${keyword}`);
return res;
}
export async function getStaticProps() {
const rank = 5;
const keyword = "shorts";
const response = await getData(rank, keyword);
const products = await response.json();
console.log(products);
if (!products) {
return {
notFound: true,
}
}
return {
props: {
products,
},
}
}
export default function AllProducts(stuff) {
let {products} = stuff;
const [rank, setRank] = useState("3");
const [keyword, setKeyword] = useState("shoes");
//from form
const updateParams = async (e) => {
e.preventDefault();
const response= await getData(rank, keyword);
products = await response.json();
}
return (
<div>
<input
type='text'
placeholder='topRank'
value={rank}
onChange={e => setRank(e.target.value)}
/>
<input
type="text"
placeholder='searchTerm'
value={keyword}
onChange={e => setKeyword(e.target.value)}
/>
<button
type='submit'
onClick={updateParams}>
Update Params</button>
<ul>
{products.Products.map((product) => {
return (
<div key={product.Id}>
<li>{product.Name}</li>
<li><img width={300} src={ product.imgUrl } alt="product image" /></li>
</div>
) }
)}
</ul>
</div>
)
}
getStaticProps is run at build-time so it'll provide the data that's available at that time. To update the UI after the user interacts with the form you should put products into state and update it once new params are submitted and you retrieve the new products.
// all-products.js - removed irrelevant code for simplicity
export default function AllProducts(stuff) {
const [products, setProducts] = useState(stuff.products);
//...
const updateParams = async (e) => {
e.preventDefault();
const response = await getData(rank, keyword);
const newProducts = await response.json();
setProducts(newProducts);
}
return (
//...
<ul>
{products.Products.map((product) => {
return (
<div key={product.Id}>
<li>{product.Name}</li>
<li><img width={300} src={product.imgUrl} alt="product image" /></li>
</div>
)
})}
</ul>
//...
)
}
I am fetching data using promise and setting the data using useState as shown below. How can use async/await instead of promise then?
// App
import React, { useState } from 'react';
import fetchEmails from 'data/fetchEmails';
const App = () => {
const [date, setDate] = useState('');
const [data, setData] = useState([]);
return (
<div>
<div>
<input
value={date}
onChange={setDate}}
/>
<button onClick={() => fetchEmails(date, setData)}>Get data</button>
</div>
<div>
{/* show data here, please ignore this part */}
{data.map(d => <div>{d.text}</div>)}
</div>
</div>
);
};
// fetchEmails
const fetchEmails = (date, setData) => {
fetch(
`http://localhost:9000/?date=${date}`
)
.then((res) => res.json())
.then((res) => setData(res))
.catch((err) => console.log(err));
};
export default fetchEmails;
you can do like this
const fetchEmails = async (date, setData) => {
let res = await fetch(`http://localhost:9000/?date=${date}`);
console.log(res);
setData(res);
};
const [src, setSrc] = useState(null);
const getSrc = async () => {
const response = await fetch(`https://aws.random.cat/meow`);
const data = await response.json();
setSrc(data.file);
};
const onClick = () => getSrc();
return (
<div>
<img src={src} alt={src} />
<button onClick={onClick}>click</button>
</div>
);
const onClick = useCallback(() => getSrc())
return <button onClick={onClick}></button>