I'm trying to build a game deals watcher and I been working on my browse page. I want to add a filter based on price and title in one fetch request. However, I'm not sure how to accomplish that in my case. Here's my Browse.jsx file:
import React, { useState, useEffect } from 'react';
function Browse({currentUser}) {
const [gameDealsList, setGameDealsList] = useState([]);
const [gameTitle, setTitle] = useState('')
// const [minPrice, setMinPrice] = useState('')
// const [maxPrice, setMaxPrice] = useState('')
const defaultURL = `https://www.cheapshark.com/api/1.0/deals?`
useEffect(()=>{
fetch(defaultURL)
.then((r)=>r.json())
.then((gameList)=> setGameDealsList(gameList))
},[])
console.log(gameDealsList)
function handleRedirect(e, dealID){
e.preventDefault();
window.open(`https://www.cheapshark.com/redirect?pageSize=10&dealID=${dealID}`, '_blank');
return null;
}
return(
<div className="container-fluid">
<h1>Browse</h1>
<h4>Filter:</h4>
<input placeholder='Title' value={gameTitle} onChange={(e)=>setTitle(e.target.value)}></input>
<span>Price Range $:</span>
<input
type="range"
className="price-filter"
min="0"
value="50"
max="100"
/>
<br/><br/>
{gameDealsList.map((game) =>
<div className="container" key={game.dealID}>
<div className="row">
<div className="col">
<img src={game.thumb} className="img-thumbnail" alt='thumbnail'/>
</div>
<div className="col">
<strong><p>{game.title}</p></strong>
</div>
<div className="col">
<span><s>${game.normalPrice}</s></span><br/>
<span>${game.salePrice}</span><br/>
<span>{Math.round(game.savings)}% Off</span>
</div>
<div className="col">
<button onClick={(e)=>handleRedirect(e, game.dealID)}>Visit Store</button>
</div>
<div className="col">
{currentUser ? <button>Add to wishlist</button> : null}
</div>
</div><br/>
</div>
)}
</div>
)
}
export default Browse;
Right now, I'm only fetching deals without any filters. However, the API allows me to set filters. For instance, if I want to search deals based on video game title I can just add &title={random title}. Also, I can type in &upperPrice={maximum price} to set up max price of deals. So, I would like to figure out ways to implement these filters in my fetch request without writing multiple fetch requests.
CodePudding user response:
You can try this approach. Only 1 comment in the code to be worried about.
Another thing to worry is to add a Debounce to a fetch
function, because without that requests will be sent every time variables in depsArray
changed, so if i try to type Nights - 6 requests will be sent while im still typing.
In order to have everything working well:
- Create some utils.js file in order to keep some shared helper functions. For
debounce
in our case.
utils.js
export function debounce(func, wait) {
let timeout;
return function (...args) {
const context = this;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args);
}, wait);
};
}
- Import and wrap the function we want to debounce with
useCallback
and actualdebounce
:
import { debounce } from "./utils";
/* ... */
const fetchDeals = useCallback((queryObject) => {
const url = new URL(`https://www.cheapshark.com/api/1.0/deals`);
for (const [key, value] of Object.entries(queryObject)) {
if (value) url.searchParams.append(key, value);
}
console.log(url);
return fetch(url)
.then((r) => r.json())
.then((gameList) => setGameDealsList(gameList));
}, []);
const fetchDealsDebounced = useMemo(() => {
// So API call will not be triggered until 400ms passed since last
// action that may trigger api call
return debounce(fetchDeals, 400);
}, [fetchDeals]);
// Initial call will still be executed but only once, even if we have
// 3 items in depsArray (due to debounce)
useEffect(() => {
// Name object keys according to what API expects
fetchDealsDebounced({ title: gameTitle, upperPrice: maxPrice });
}, [fetchDealsDebounced, gameTitle, maxPrice]);
CodePudding user response:
You should be able to append the query parameters directed by the API to your default query string
fetch(defaultURL new URLSearchParams({
lowerPrice: minPrice,
upperPrice: maxPrice,
title: gameTitle,
}).then()...
As far as how to control this with only one request you could refactor useEffect like this.
useEffect(() => {
const queryParams = {
lowerPrice: minPrice,
upperPrice: maxPrice,
title: gameTitle,
};
if (minPrice === '') delete queryParams.lowerPrice;
if (maxPrice === '') delete queryParams.upperPrice;
if (gameTitle === '') delete queryParams.title;
fetch(defaultURL new URLSearchParams(queryParams).then()...
}, [maxPrice, minPrice, gameTitle]);