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
}
}
Related
When I fetch data from firebase firestore using getStaticProps, it works perfectly but when I try implementing the logic of getting the details of each single item using getStaticPaths, I fail and get a 404 page. This is how my [id].js code looks like currently.
import React from 'react'
import { db } from '#/Firebase';
import {collection, getDoc} from "firebase/firestore";
const reference = collection(db, "abantu");
export const getStaticPaths= async () => {
const umuntu = await getDoc(reference);
const paths = umuntu.docs.map(doc => {
return {
params: { id: doc.id }
}
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async (context) => {
const id = context.params.id;
const data = await getDoc(reference) + id;
return {
props: {
umuntu: data
}
}
}
function Details({umuntu}) {
return (
<div>
<h1>{umuntu.ibizo}</h1>
</div>
)
}
export default Details
I dont quite get where my logic is going wrong but where could I be going wrong?.
For finding the right page props for each of the paths that you generate from the database in the getStaticPaths function, you should be able to find each of the pages information based on the id field you are getting from each path, see it here:
export const getStaticProps = async (context) => {
const id = context.params.id;
const umuntu = await getDoc(reference);
const data = umuntu.docs.find((pageData) => pageData.id === id); // this will find the right page based on the id passed via page path
return {
props: {
data
},
};
};
function Details({ data }) {
return (
<div>
<h1>{data.ibizo}</h1>
</div>
);
}
export default Details;
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.
I'm trying to render a page with two props from different API fetches.
The adress bar looks like this: http://localhost:3000/startpage?id=1
And the code looks like this, with the first API fetch:
import { useRouter } from "next/router";
export const getServerSideProps = async (context) => {
const { id } = context.query;
const res = await fetch(`${process.env.BACKEND_URL}/User/${id}`);
const data = await res.json();
// console.log(data);
return {
props: { user: data },
};
};
Second API fetch looks like this
export const getServerSideProps2 = async (context) => {
const { id } = context.query;
const res = await fetch(`${process.env.BACKEND_URL}/User/${id}/favorites`);
const data = await res.json();
//console.log(data);
return {
props: { favorites: data },
};
};
And the page that I am trying to render then looks like this:
function StartPage( {user, favorites} ){
return (
<div>
<div className={styles.formGroup}>
<h1>Welcome {user.name}</h1>
</div>
<div>
<h1>These are your favorite movies:</h1>
{favorites.map(favorite => (
<div key={favorite.id}>
<h5>favorite.name</h5>
</div>
))}
</div>
</div>
)
}
I'm guessing that there's a way to put both API fetches in the same function. But I don't know how to. If anyone has any suggetions on how to do that I'd be happy to listen.
Thank you in advance.
You can make the calls in the same method and pass both data:
export const getServerSideProps = async (context) => {
const { id } = context.query;
const res = await fetch(`${process.env.BACKEND_URL}/User/${id}`);
const data = await res.json();
const resFav = await fetch(`${process.env.BACKEND_URL}/User/${id}/favorites`);
const dataFav = await resFav.json();
return {
props: { user: data, favorites: dataFav },
};
};
No need to declare getServerSideProps2
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,
},
};
};
This is my first time using this website so bear with me...
I am trying to display an array of 10 youtube videos from a playlist using the youtube api. So far I am able to console.log the json data from the api. Although the issue arrives when I attempt to display that json data in my react app using inside a map function.
Here is all my code :
import React, { Component } from "react";
import './Videos.css';
import VideosCP from '../Pages/VideosPage/VideosSearch'
import youtubeSearch from "../../api/youtubeSearchApi";
import VideosList from "../Pages/VideosPage/VideoList";
const YOUTUBE_PLAYLIST_ITEMS_API = 'https://www.googleapis.com/youtube/v3/playlistItems';
let url = `${YOUTUBE_PLAYLIST_ITEMS_API}?part=snippet&playlistId=myPlaylistId&maxResults=10&key=myYoutubekey`
function Demo() {
}
class Videos extends Component{
state = {
videoMetaInfo:[],
selectedVideoId: null
}
onVideoSelected = videoId => {
this.setState({
selectedVideoId:videoId
})
}
onSearch = async keyword => {
const response = await youtubeSearch.get("/search",{
params:{
q:keyword
}
})
console.log(this.state);
this.setState({
videoMetaInfo: response.data.items,
selectedVideoId: 'Nl3DoewG3Co'
})
}
playlistRender = async () => {
const data = await fetch(url);
let commits = await data.json();
console.log('data', commits)
}
render(){
const Data = ({data}) => {
data = this.playlistRender()
const results = data.items.map((item)=>{
const {id, snippet = {}} = item
const title = snippet
return(
<li key={id}>
<h3>{title}</h3>
</li>
)
});
return results
}
return(
<div className="wrapper">
<div className="wrapper-inner scroll">
<VideosCP onSearch={this.onSearch}/>
<VideosList onVideoSelected={this.onVideoSelected}
data={this.state.videoMetaInfo}/>
<ul>
<Data/>
</ul>
</div>
</div>
);
}
}
export default Videos
The code compiles fine in the terminal but once inside the browser it spits the error : "Cannot read property 'map' of undefined".
Hence, the error is caused by this line which states : data.items.map((item)=>{
What's even more weirder is that the console in the browser recognizes that there is an "items" in the array. Why is the browser spitting this error at me? Can it not map the items? If so why not? And how is this error fixable?
I have tried everything so far and this is the closest I've gotten but yet no results.
Please help me, stack overflow is my only hope...
The data you're loading from the API is stateful, so you'll want to set it to state once it's loaded. Then, you can map over this stateful information once it exists.
I also added a componentDidMount method call to make sure we call the API once the component mounts.
class Videos extends Component{
state = {
videoMetaInfo:[],
selectedVideoId: null,
commits: []
}
onVideoSelected = videoId => {
this.setState({
selectedVideoId:videoId
})
}
onSearch = async keyword => {
const response = await youtubeSearch.get("/search",{
params:{
q:keyword
}
})
console.log(this.state);
this.setState({
videoMetaInfo: response.data.items,
selectedVideoId: 'Nl3DoewG3Co'
})
}
playlistRender = async () => {
const data = await fetch(url);
let commits = await data.json();
this.setState({ commits });
}
componentDidMount() {
this.playlistRender();
}
render(){
return(
<div className="wrapper">
<div className="wrapper-inner scroll">
<VideosCP onSearch={this.onSearch}/>
<VideosList onVideoSelected={this.onVideoSelected}
data={this.state.videoMetaInfo}/>
<ul>
{this.state.commits.map((item) => {
const {id, snippet = {}} = item
const title = snippet
return (
<li key={id}>
<h3>{title}</h3>
</li>
)
})}
</ul>
</div>
</div>
);
}
}
export default Videos