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>
//...
)
}
Related
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;
When I click on the Delete button, my code does not work. There could be a problem in the function handleRemove.
import React, { useState, useEffect } from 'react'
import axios from 'axios'
// API endPoint - Punk API
const API_URL = 'https://api.punkapi.com/v2/beers'
const List = () => {
const [drinks, setDrinks] = useState([])
const [searchTerm, setSearchTerm] = useState('')
const fetchData = async () => {
const { data } = await axios.get(API_URL)
setDrinks(data)
}
useEffect(() => {
fetchData()
}, [])
const handleRemove = (id) => {
let groupd = drinks
const newList = groupd.filter(group => group.id !== id)
setDrinks(newList)
}
return (
<div>
<div className="wrapper">
<div className="search__main">
<input type='text' placeholder="search..." onChange={e => {setSearchTerm(e.target.value)}}/>
</div>
</div>
<div className="wrapper">
<div className="search__box">
{drinks.filter((val) => {
if(searchTerm === ""){
return val
} else if(val.name.toLowerCase().includes(searchTerm.toLowerCase()) || val.description.toLowerCase().includes(searchTerm.toLowerCase())){
return val
}
}).map((drink, key) => {
return(
<div key={key} className="search__mini__box">
<div >
<img src={drink.image_url} alt="drink" className="search__img"/>
</div>
<h4>{drink.name}</h4>
<p>{drink.description}</p>
<button type="button" onClick={handleRemove(drink.id)}>
delete
</button>
</div>
)
})}
</div>
</div>
</div>
)
}
export default List
Since your handleRemove function call is within a return statement, you need to call the function like so:
onClick={() => handleRemove(drink.id)}
What happens is, the function is called immediately on render if done the way you've proposed in your question. We want the function to be called only when the button is clicked.
function DataWeather({ weather, setWeather }) {
const cityName = weather.city.name;
const countryName = weather.city.country;
const minTemp = weather.list.main.temp_min;
const maxTemp = weather.list.main.temp_max;
const handleRemoveItem = (id) => {
setWeather((weather) => weather.filter((city) => city.id !== id));
};
return (
<div>
<div>
<span onClick={handleRemoveItem}>
x
</span>
<p>
{cityName} {countryName}
</p>
<p>MaxTemp : {maxTemp}</p>
<p>MinTemp: {minTemp}</p>
</div>
</div>
);
}
export default DataWeather;
Hey, I'm trying to remove city from coming api, but i get error that filter is not a function. Anyone has idea why I get this error. I have initialize weather with useState([]).
function WeatherData() {
const [query, setQuery] = useState("");
const [weather, setWeather] = useState({});
const FetchData = async (e) => {
try {
const response = await fetch(
`https://api.openweathermap.org/data/2.5/forecast?q=${query},&appid=${YOU_API}`
);
const weatherData = await response.json();
setWeather(weatherData);
} catch (err) {
console.log(err);
}
};
return (
<div>
<main>
<form onSubmit={FetchData}>
<input
type="text"
placeholder="Click"
onChange={(e) => setQuery(e.target.value)}
value={query}
/>
{Object.entries(weather).length !== 0 ? (
<DataProfile weather={weather} setWeather={setWeather} />
) : (
<h3> Please City Name </h3>
)}
<button className="btn">Click</button>
</form>
</main>
</div>
);
}
as you can see here is the rest of my code fetching the weather api to get the detail of this api. I just want to remove the data by clicking the X.
Because weather is an object so you can't delete it with filter.
You can use spread operator to update object like this:
const handleRemoveItem = (id) => {
setWeather((weather) => ({...weather, city: {}}));
};
I'm fetching two things. An item by id and then the item comments by item id. I when npm start I get
TypeError: data.comments is undefined
But if I comment out
<Comment data={itemComments} />
And then run npm start, the item data loads and if I uncomment the comment tag after the item data has already loaded comments shows until I refresh or reload again, it's only when I try to load them simultaneously I get the error.
Item.js
import React, { useEffect, useState } from "react";
import Comment from "./Comment";
import axios from "axios";
const Item = () => {
const itemId = "6019afbce548e33e7c2f4e56";
const [item, setItem] = useState([]);
const [itemComments, setItemComments] = useState([]);
const fetchData = () => {
const item = `http://localhost:3000/api/v1/items/${itemId}`;
const itemComments = `http://localhost:3000/api/v1/items/${itemId}/comments`;
const getItem = axios.get(item);
const getItemComments = axios.get(itemComments);
axios.all([getItem, getItemComments]).then(
axios.spread((...allData) => {
const allItemData = allData[0].data;
const allItemCommentsData = allData[1].data;
setItem(allItemData);
setItemComments(allItemCommentsData);
})
);
};
useEffect(() => {
fetchData();
}, []);
return (
<div>
<div>
<div>
<h3>{item.title}</h3>
</div>
<div>
<p>Price</p>
<p>${item.price}</p>
</div>
<div>
<h3>Offers & Comments</h3>
<Comment data={itemComments} />
</div>
</div>
</div>
);
};
export default Item;
ItemComments.js
import React from "react";
const Message = (props) => {
const { data } = props;
console.log(data);
return (
<>
{data &&
data.comments.map((comment, i) => (
<div key={i}>
<div>
<div>
<p>{comment.comment}</p>
</div>
</div>
</div>
))}
</>
);
};
export default Message;
After first render react try to access comments inside itemComments when its just an empty array, and you just check if its not undefined in your children component:
{data && data.comments.map((comment, i) => (
<div key={i}>
<div>
<div>
<p>{comment.comment}</p>
</div>
</div>
</div>
))}
so change your initial state to this:
const [itemComments, setItemComments] = useState({comments:[]});
name your comment state by its initial name like this "useState({comments:[]})" and make sure that data.comment is not empty and also try to make fetch data asynchronously and let me know the result, please
const fetchData = async () => {
const item = `http://localhost:3000/api/v1/items/${itemId}`;
const itemComments = `http://localhost:3000/api/v1/items/${itemId}/comments`;
const getItem = await axios.get(item);
const getItemComments = await axios.get(itemComments);
const allData=await axios.all([getItem, getItemComments])
const allItemData= await axios.spread((...allData) => allData[0].data)
const allItemCommentsData= await axios.spread((...allData) => allData[1].data)
setItem(allItemData);
setItemComments(allItemCommentsData);
};
and thank you in advance for any help.
I am trying to build a web app that fetches data from an API, in this case a movie database API, but when i am trying to map all the movies from a specific title search i get the .map is not a function error, what i am doing wrong ? Can't i use useState to display the data ?
When i do console.log (search) i can see the array with all the data :
import React, {useEffect, useState} from 'react';
import axios from 'axios';
export default function RandomFacts() {
const [input, setInput] = useState('');
const [search, setSearch] = useState(['']);
useEffect(() => {
apiCall();
}, [input]);
const moviesList = search && search.map((movie, index) =>
<div className="movies" key="index">
<li><h2>{movie.Title}</h2></li>
<li><img src={movie.Poster} alt="poster" /></li>
</div>,
);
const apiCall = async () => {
const url = 'http://www.omdbapi.com/?s='+input+'&page=1&apikey=536a34c3';
try {
const response = await axios.get(url);
if (response.status === 200 && response !== undefined) {
const data = response.data;
setSearch(data.Search);
console.log(search);
}
} catch (error) {
console.log(error);
}
};
return (
<div className="main">
<h1>Movies</h1>
<div className="textInput">
<form>
<label>
<input type="text" value={input}
onChange={(e) => setInput(e.target.value)}
/>
</label>
</form>
</div>
<div className="movies">
{moviesList}
</div>
</div>
);
}
The API is returning a response Object with a data key containing the keys Search, TotalResults, and Response. You're trying to map this response Object instead of the Array contained in response.data.Search.
So you should be using setSearch(response.data.Search).