useQuery Hook result set to loading without being called - javascript

I'm trying to build a search bar in my new project, and I seem to be doing some things(maybe a lot) wrong.
I set the book state to null and the useQuery hook seems to be using it to search for books.
I don't want it to search for anything unless I click the button.
These are my codes:
fetchBooks.jsx
async function fetchBooks({ queryKey }) {
const book = queryKey[1];
const response = await fetch(
`https://www.googleapis.com/books/v1/volumes?q=${book}`
);
if (!response.ok) {
throw new Error(`Search not found for ${book}`);
}
return response.json();
}
export default fetchBooks;
Here is the main component.
import { useState } from "react";
import { useQuery } from "#tanstack/react-query";
import fetchBooks from "../helper/fetchBooks";
const Home = () => {
const [book, setBook] = useState(null);
const results = useQuery(["search", book], fetchBooks);
const handleSubmit = (e) => {
e.preventDefault();
setBook(e.target.elements.book.value);
};
return (
<>
<form onSubmit={handleSubmit}>
<label htmlFor="book">
Book Name:
<input type="text" name="book" />
</label>
<button type="submit">Submit</button>
</form>
{results.isLoading ? (
<div>Loading...</div>
) : results.isError ? (
<div>{results.error.message}</div>
) : (
<div>
<h2>Results</h2>
<ul>
{results.data.items.map((item) => {
return (
<div key={item.id}>
<h3>{item.volumeInfo.title}</h3>
<p>{item.volumeInfo.authors}</p>
</div>
);
})}
</ul>
</div>
)}
</>
);
};
export default Home;

You can return a default value in the fetch function if the book is null. Then, the query won't actually request the API.
async function fetchBooks({ queryKey }) {
const book = queryKey[1];
if(!book) return { items: [] }
const response = await fetch(
`https://www.googleapis.com/books/v1/volumes?q=${book}`
);
if (!response.ok) {
throw new Error(`Search not found for ${book}`);
}
return response.json();
}
export default fetchBooks;

Instead of restricting the useQuery to call the fecthBooks functions, you can modify the fetchBooks functions to return an empty array if book is set to null. The fetchBooks can be modified as below:-
async function fetchBooks({ queryKey }) {
const book = queryKey[1];
if(!book){
return {
isLoading : false,
error : null,
data : null
}
}
const response = await fetch(
`https://www.googleapis.com/books/v1/volumes?q=${book}`
);
if (!response.ok) {
throw new Error(`Search not found for ${book}`);
}
return response.json();
}
export default fetchBooks;

The idiomatic way would be to set the useQuery itself to disabled (via the enabled property) when your params aren't ready:
const results = useQuery({
queryKey: ["search", book],
queryFn: fetchBooks
enabled: !!books
})
this will prevent the query function from executing when you have no books.

Related

How to pass the data input from one component into another component?

Introducing The Problem
I am beginner ReactJS learner developing a simple weather app using OpenWeather API. The app is designed to fetch data from two components: one that returns the current weather of the user input and another one that returns the weather forecast for the next 5 days.
When the city name is typed down into the input field, the following message appears on the console:
GET https://api.openweathermap.org/data/2.5/weather?q=undefined&units=metric&appid=${Api.key} 400 (Bad Request)
I do not know how to pass the data from Search Component into App Component. Seriously, I have tried a lot of alternatives but they have been unsuccessful. There are commented lines of code to show my last try so far.
(ignore ForecastWeather because this component is empty)
I know that all of you are quite busy folks, but I would appreciate the help in a respectful way. Even suggestions about what I have to study (e.g. callBack) are welcome. I've tried this already:
https://stackoverflow.com/questions/56943427/whether-to-save-form-input-to-state-in-onchange-or-onsubmit-in-react
https://sebhastian.com/react-onchange/
The code is forward below:
App.js
import React, { useState } from "react";
import { Api } from "./Api";
import {
Search,
CurrentWeather,
ForecastWeather,
Footer,
} from "./components/index";
import "./App.css";
function App() {
const [getCity, setGetCity] = useState();
const [weatherData, setWeatherData] = useState(null);
const [forecastData, setForecastData] = useState(null);
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${getCity}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${getCity}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setGetCity(dataSearch);
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
return (
<div className="App">
<Search
searchResultData={handleSearchLocation}
textPlaceholder="Search for a place..."
/>
{weatherData && <CurrentWeather resultData={weatherData} />}
<ForecastWeather resultData={forecastData} />
<Footer />
</div>
);
}
export default App;
Search.jsx
import React, { useState } from "react";
function Search({ textPlaceholder, searchResultData }) {
const [searchCity, setSearchCity] = useState("");
//const handlerOnChange = ( event, dataSearch ) => {
//setSearchCity(event.target.value);
//setSearchCity(dataSearch);
//searchResultData(dataSearch);
//};
return (
<div className="componentsBoxLayout">
<input
value={searchCity}
//onChange={handlerOnChange}
onChange={(event) => setSearchCity(event.target.value)}
onKeyDown={(event) => event.key === "Enter" && searchResultData(event)}
placeholder={textPlaceholder}
/>
</div>
);
}
export default Search;
CurrentWeather.jsx
import React from "react";
function CurrentWeather({ resultData }) {
return (
<div className="componentsBoxLayout">
<p>{resultData.name}</p>
</div>
);
}
export default CurrentWeather;
ForecastWeather.jsx (empty)
import React from 'react';
function ForecastWeather() {
return (
<div className="componentsBoxLayout">ForecastWeather</div>
)
}
export default ForecastWeather;
Api.js
const Api = {
url: "https://api.openweathermap.org/data/2.5",
key: "etcetc",
img: "https://openweathermap.org/img/wn",
};
export { Api };
Yippee-ki-yay
You can not use getCity in this function:
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${getCity}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${getCity}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setGetCity(dataSearch);
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
getCity is defined on that function so it does not exist when you try to use it, unless you need getCity later for another component I would delete it becuase is redundant and do this:
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${dataSearch}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${dataSearch}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
When you run searchResultData on the search component you send the city you are looking for. Remember that useState will trigger a re-render but a function that is already running before that will never get the new value of the state if the state changes

Next JS [id] error Error serializing `.data` returned from `getServerSideProps` in "/services/[id]"

I make Next JS project and I am new to coding in this program and have a "Service" folder. In this folder there are index.js and [id].js (details page). All data come from Next API. Index.js works, there is no problem. But when I click the details element the error is seen. I don't know what is my mistake
Error: Error serializing `.data` returned from `getServerSideProps` in "/services/[id]". Reason: `object` ("[object Promise]") cannot be serialized as JSON. Please only return JSON serializable data types.
index.js
<section className="services-main">
<div className="services-main-context container">
<MainPageServices posts={posts} />
</div>
</section>
....
export async function getStaticProps() {
const res = await fetch("http://localhost:3000/api/servicesApi/");
const posts = await res.json();
return {
props: {
posts,
},
};
}
MainPageServices component
<div className="main-page-services-cards">
{posts.map((card, key) => (
<div key={card.id} className="service-card">
<Link href={`/services/${card.id}`}>
<a>
<div className="card-img">
<Image src={card.img} alt="Services" />
</div>
</a>
</Link>
</div>
))}
</div>
Not working component (Details)
const ServiceDetails = ({ data }) => {
console.log(data);
return (
<h1>{data.header}</h1>)
);
};
export const getServerSideProps = async (context) => {
const res = await fetch(`http://localhost:3000/api/servicesApi/${context.params.id}`);
const data = res.json();
return {
props: {
data,
},
};
};
My details page API
import { servicesData } from "../../../data";
export default function handler(req, res) {
const { id } = req.query;
const service = servicesData.find((service) => service.id === parseInt(id));
res.status(200).json(service);
}
I think you need to await res.json() because your error says you are passing a promise into your props.
const ServiceDetails = ({ data }) => {
console.log(data);
return (
<h1>{data.header}</h1>)
);
};
export const getServerSideProps = async (context) => {
const res = await fetch(`http://localhost:3000/api/servicesApi/${context.params.id}`);
const data = await res.json();
return {
props: {
data,
},
};
};

map function not working in ReactJs using nexttjs

I am new in ReactJS and i am using "Nextjs" framework,Right now i am using "async" function
for fetching data but unable to fetch using "map" function, in console.log ...showing me following message
" items: undefined }",Here is my code,Where i am wrong ?
import React, { Component } from 'react';
const blog =({items}) =>{
console.log({items});
return(
<div>
</div>
);
};
//calling api for get data
export const getstaticprops=async()=>{
console.log('Om Success');
const res=await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await res.json()
return {
props: { items: posts },
}
}
export default blog
test this code !
const blog =({posts}) =>{
console.log(posts); \\ fix console log
return(
<div>
</div>
);
};
export async function getServerSideProps(){
console.log('Om Success');
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await res.json()
return {
props: { posts }, \\ remove items
}
}

Destructuring specific data from a react component

I would like to be able to make both data.account and data.assets accessbile individually. The idea is to be able to insert this component into a page where I can say something like:
import UserAccounts from '...';
"This user (**data.account**) owns this number (**data.assets**) of assets".
The following is the component code:
function UserAccounts() {
const [accounts, setAccounts] = useState();
useEffect(() => {
async function fetchData() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicassets/v1/accounts?limit=10'
);
const { data } = await res.json();
setAccounts(data);
}
fetchData();
}, []);
if (!accounts) {
return (
<div>
<Spinner />
</div>
);
}
const account = accounts.map((data) => {
return <>{(data.account, data.assets)}</>;
});
return <></>;
}
export default UserAccounts;
the way that it is now, it's pulling the accounts from the api. The problem is that it's not set to return anything. I'm trying to figure out how I can decide which to return based on what I need (data.account or data.assets

React Query with server side rendering using Next.js

I am trying to use react-query with nextjs to prefetch query on server. It works for the initial query which gets a list of items. However when I try to fetch each item inside component it only fetches it on the client side.
export default function Home() {
const { data } = useQuery("pokemons", fetchPokemons);
return (
<>
<div>
{data.map((pokemon) => (
<Pokemon key={pokemon.name} pokemon={pokemon}/>
))}
</div>
</>
);
}
export async function getStaticProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery('pokemons', fetchPokemons)
const fetchedPokemons = queryClient.getQueryData()
//query each pokemon
fetchedPokemons.forEach(async (pokemon) => {
await queryClient.prefetchQuery(pokemon.name, () => fetchPokemon(pokemon.url))
});
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
And here is code for the component which also queries each item.
const Pokemon = ({pokemon}) => {
const {data} = useQuery(pokemon.name, () => fetchPokemon(pokemon.url))
// logs only in browser, on server it is undefined
{console.log(data)}
return (
<div>
<h3>
Name - {data.name}
</h3>
<h4>Base XP - {data.base_experience}</h4>
</div>
)
}
Can you please tell me what am I doing wrong that the query doesn't execute on server or is it an issue of the library itself?
when you use getQueryData to get data from the cache, you need to provide the key of the data you want to get:
await queryClient.prefetchQuery('pokemons', fetchPokemons)
const fetchedPokemons = queryClient.getQueryData('pokemons')
alternatively, you can use fetchQuery to also retrieve the data immediately
try {
const fetchedPokemons = await queryClient.fetchQuery('pokemons')
} catch (error) {
// handle error
}
Be aware that fetchQuery throws errors (as opposed to prefetchQuery, which does not), so you might want to handle errors somehow.
I was able to solve this by combining two of my fetching functions into one like so
const fetchPokemons = async () => {
const { data } = await axios.get(
"https://pokeapi.co/api/v2/pokemon?limit=10&offset=0"
);
const pokemonArray = await Promise.all(
data.results.map(async (pokemon) => {
const res = await axios.get(pokemon.url);
return res.data;
})
);
return pokemonArray;
};
export default function Home() {
const { data } = useQuery("pokemons", fetchPokemons);
return (
<>
<div>
{data.map((pokemon) => (
<Pokemon key={pokemon.name} pokemon={pokemon}/>
))}
</div>
</>
);
}
export async function getStaticProps() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery("pokemons", fetchPokemons);
return {
props: {
dehydratedState: dehydrate(queryClient),
},
};
}

Categories

Resources