How do i create a conditional loading message in react - reactjs

I have a react code here
i want to load data from an API but it's taking time to get the data, thus my function is failing
How do i set it that it should wait for the data before rendering
import Head from 'next/head'
import Link from 'next/link'
import Navbar from './Navbar'
import Template from './Template'
import { useState, useEffect } from 'react'
export async function getStaticProps() {
const response = await fetch('https://peegin.com/api/public/peegins/recent')
const data = await response.json()
return {
props: { data }
}
}
const Home = ({ data }) => {
return (
<div className="content">
<Head>
<title>Peegin Recent</title>
</Head>
<Navbar />
{title}
{data.map(peegin => (
<div className="preview" key={peegin.permalink}>
<h3 className="title">
{peegin.title}
</h3>
<p>{peegin.meaning}</p>
<p className="example">Example</p>
<p className="example-content">{peegin.example}</p>
<p className="origin">Origin: {peegin.origin}</p>
<div className="name">
<h4>By</h4> <h4 className="namegreen">{peegin.user.name}</h4> <h4>{peegin.created_at}</h4>
</div>
<p className="views">{peegin.views.view} Views</p>
</div>
))}
</div>
);
}
export default Home;

const Home = ({ data }) => {
// add this as fallback
if(!data){
return <h1>Loading..<h1/>
}
return (
<div className="content">
<Head>
<title>Peegin Recent</title>
</Head>
<Navbar />
{title}
// add this to check for data first then do map
{data && data.map(peegin => (
<div className="preview" key={peegin.permalink}>
<h3 className="title">
{peegin.title}
</h3>
<p>{peegin.meaning}</p>
<p className="example">Example</p>
<p className="example-content">{peegin.example}</p>
<p className="origin">Origin: {peegin.origin}</p>
<div className="name">
<h4>By</h4> <h4 className="namegreen">{peegin.user.name}</h4> <h4>{peegin.created_at}</h4>
</div>
<p className="views">{peegin.views.view} Views</p>
</div>
))}
</div>
);
}

Related

why link doesn't work when I use getStaticProps?

I'm new in next js and I'm working on project using NextJS. I have some lots in my items page that shows lots currently(pagination/items.tsx) and I also have lotDetails page that I want it to show details of each lot using dynamic route(lotDetails\id\index.tsx).
This is the folder structure:
Now when I click the Link in Items.tsx I expect it to go to lotDetails page and pass the props, but nothing happens! (It stays on Items page!). here is Items.tsx:
import React from 'react'
import Link from "next/link"
const Items = ({currentItems}:{currentItems:any}) => {
console.log(currentItems)
// const ids=currentItems.map((el:any)=>el.id)
// const paths=ids.map((el:any)=>{params:{id:el.toString()}})
// console.log(paths)
return (
<>
<div className="container">
<div className="row">
{currentItems.map((el:any)=><div className="col-md-3 ">
//this should be linked to lotDetails page!
<Link href={{pathname:"../lotDetails/[id]",query:{id:JSON.stringify(el.id),title:el.title,image:el.image,description:el.description.toString(),rate:el.rating.rate,count:el.rating.count,price:el.price},}} as={`/lotDetails/${el.id.toString()}`}>
<div className="lot">
<div className="img-container">
<img src={el.image}/>
</div>
<div className="title">
{el.title}
</div>
<div className="price">
<span className="price-title">Price:</span>
<span>{el.price}</span>
</div>
</div>
</Link>
</div>)}
</div>
</div>
</>
)
}
export default Items;
I'm using getStaticProps and GetStaticPaths in lotDetails:
const LotDetails = (props:any) => {
const dispatch=useDispatch();
console.log(props)
const lotCount=1;
const addLots=()=>{
dispatch(AddCustomerLot({...props,lotCount:lotCount}))
}
return (
<>
<div className='container lot-details'>
<div className="row" >
<div className="col-md-6">
<div className="detail-container">
<div className="title-details"><h3>{props.title}</h3></div>
<div className="badge"><FontAwesomeIcon icon={faStar}/><span>{props.rate}</span></div>
<div className="inventory">
Inventory: <span className="count">{props.count}</span>
</div>
<div className="description-details">{props.description}</div>
<div className="price">Price: <span className="price-number">{props.price}$</span> </div>
<button className="btn btn-regist" onClick={addLots}>Add to shopping basket</button>
</div>
</div>
<div className="col-md-6"><img src={props.image} alt="" /></div>
</div>
</div>
</>
)
}
export const getStaticPaths:GetStaticPaths=async(context:any)=>{
const response= await axios.get("https://fakestoreapi.com/products")
const paths=response.data.map((el:any)=>({params:{id:el.id.toString()}}))
console.log(paths)
return{
paths,
fallback:'blocking'
}
}
export const getStaticProps:GetStaticProps=async(context:any)=>{
return{
props:
{
//recieve props
id:context.query.id,
title:context.query.title,
image:context.query.image,
description:context.query.description,
rate:context.query.rate,
count:context.query.count,
price:context.query.price
}
}
}
export default LotDetails;
when I deleted getStaticProps and getStaticPaths, the link worked! So I receive that link works perfectly and the problem is in getStaticProps and getStaticPaths. Of course I don't want to use getServerSideProps.
Update
According to julio's suggestion I changed pathName, and I also changed context.query to context.params.id:
Items:
<Link href={{pathname:`/lotDetails/${el.id}`,query:{id:JSON.stringify(el.id),title:el.title,image:el.image,description:el.description.toString(),rate:el.rating.rate,count:el.rating.count,price:el.price},}} as={`/lotDetails/${el.id.toString()}`}>
<div className="lot">
<div className="img-container">
<img src={el.image}/>
</div>
<div className="title">
{el.title}
</div>
<div className="price">
<span className="price-title">Price:</span>
<span>{el.price}</span>
</div>
</div>
</Link>
lotDetails:
return (
<>
<div className='container lot-details'>
<div className="row" >
<div className="col-md-6">
//I removed all section which used props and querys using comments
{/* <div className="detail-container">
<div className="title-details"><h3>{props.title}</h3></div>
<div className="badge"><FontAwesomeIcon icon={faStar}/><span>{props.rate}</span></div>
<div className="inventory">
Inventory: <span className="count">{props.count}</span>
</div>
<div className="description-details">{props.description}</div>
<div className="price">Price: <span className="price-number">{props.price}$</span> </div>
<button className="btn btn-regist" onClick={addLots}>Add to shopping basket</button>
</div> */}
</div>
{/* <div className="col-md-6"><img src={props.image} alt="" /></div> */}
</div>
</div>
</>
)
}
export const getStaticPaths:GetStaticPaths=async(context:any)=>{
const response= await axios.get("https://fakestoreapi.com/products")
const paths=response.data.map((el:any)=>({params:{id:el.id.toString()}}))
console.log(paths)
return{
paths,
fallback:'blocking'
}
}
export const getStaticProps:GetStaticProps=async(context:any)=>{
return{
props:
{
id:context.params.id,
// title:context.query.title,
// image:context.query.image,
// description:context.query.description,
// rate:context.query.rate,
// count:context.query.count,
// price:context.query.price
}
}
}
Finally, I solved problem in two steps:
1-I'm using google chrome browser, so I configured chrome and added my localhost port:
I used this link: chrome://inspect/, clicked configure button and added my localhost port(in my case localhost:3000)
2-I added this code to lotDetails page(the page which I used axios)
axios.defaults.httpsAgent=new https.Agent({
rejectUnauthorized:false,
})
don't forget to import:
import https from "https"
Now It's working.

How to pass props to a component on clicking button?

I have displayed all the anime images from api, now I want if any anime result is clicked then it should open details for that particular anime. I have created component Details where I want to send details of anime.
how can I send details of anime to the component Details ?
Here is my code:
import { useEffect, useState } from 'react';
import './CSS/style.css';
import Details from './components/Details';
function App() {
const [user, setUser] = useState([])
const getUsers = async () =>{
const response = await fetch('https://ghibliapi.herokuapp.com/films')
setUser(await response.json());
}
useEffect(() => {
getUsers();
}, []);
const getDetail = (e) =>{
// what should I write here to pass props on component < Details />
console.log(e)
}
return (
<>
<h2>Anime World</h2>
<div className="container-fluid mt-5">
<div className="row text-center">
{
user.map((curElem) => {
return (
<div className="col-10 col-md-4 mt-5" key={curElem.id} >
<div className="card">
<img src={curElem.image} className="card-img-top imageSize" alt="..."/>
<div className="card-body">
<h5 className="card-title">{curElem.title} </h5>
<button onClick={getDetail} >Get Detail</button>
</div>
</div>
</div>
)
})
}
</div>
</div>
</>
);
}
export default App;
Dummy values are given to component Details right now, I have to get correct value from props.
import React from 'react';
import './details.css';
function Details() {
return (
<>
<h2>Information of Anime</h2>
<div className="container-fluid mt-5">
<div className="row text-center">
<div className="col-10 col-md-8 mx-auto" >
<div className="card box">
<img src= '...' className="card-img-top imageSize" alt="..."/>
<div className="card-body">
<div className='details'>
<h5><span>Title:</span> Title</h5>
<h5><span>Original Title:</span> Original Title</h5>
<h5><span>Director:</span> Director</h5>
<h5><span>Producer:</span> Producer</h5>
<h5><span>Release Date:</span> date</h5>
<h5><span>Running time:</span> time</h5>
<h5><span>Description:</span> <p> Description</p></h5>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default Details;
I'm going to assume that the list you receive from the API is the list of each anime, and when you click you want to render the selected anime using the Details component. If that's the case this is my approach
import { useEffect, useState } from 'react';
import './CSS/style.css';
import Details from './components/Details';
function App() {
const [user, setUser] = useState([]);
const [anime, setAnime] = useState(null);
const getUsers = async () =>{
const response = await fetch('https://ghibliapi.herokuapp.com/films');
setUser(await response.json());
};
useEffect(() => {
getUsers();
}, []);
const getDetail = (anime) =>{
setAnime(anime);
};
return (
<>
<h2>Anime World</h2>
<div className="container-fluid mt-5">
<div className="row text-center">
{user.map((elem) => (
<div className="col-10 col-md-4 mt-5" key={curElem.id}>
<div className="card">
<img src={elem.image} className="card-img-top imageSize" alt="..."/>
<div className="card-body">
<h5 className="card-title">{elem.title} </h5>
<button onClick={() => getDetail(elem)} >Get Detail</button>
</div>
</div>
</div>
))}
</div>
</div>
{anime && <Details {...anime} />}
</>
);
}
export default App;
So basically what I'm doing here is rendering <Details /> only when an anime element is in state. If it's null it won't render the component.
Secondly I'm spreading the selected element into the Details component, so that inside the component you can access each property like this:
import React from 'react';
import './details.css';
function Details(props) {
const { title } = props; // each prop is each property of the selected anime
return (
<>
<h2>Information of Anime</h2>
<div className="container-fluid mt-5">
<div className="row text-center">
<div className="col-10 col-md-8 mx-auto" >
<div className="card box">
<img src= '...' className="card-img-top imageSize" alt="..."/>
<div className="card-body">
<div className='details'>
<h5><span>Title:</span> {title}</h5>
<h5><span>Original Title:</span> Original Title</h5>
<h5><span>Director:</span> Director</h5>
<h5><span>Producer:</span> Producer</h5>
<h5><span>Release Date:</span> date</h5>
<h5><span>Running time:</span> time</h5>
<h5><span>Description:</span> <p> Description</p></h5>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default Details;

Movie' cannot be used as a JSX component

Trying to make a movie app but the type won't let me take the arguments and display them (using typescript).The problem is that I am trying to map the movie Object and display them one by one to homepage and show all the movies that I got. Also I use api from themoviedb.
This is the movie script
import React, { useEffect, useRef, useState } from "react";
import { KeyObject } from "tls";
const IMG_API="ImageApi";
interface movie{
title: string;
path: path.ParsedPath;
overview: string;
voteavg: string;
key: KeyObject;
}
const Movie=(movie:movie)=>{
<div className="movie">
<img src={IMG_API+movie.path} alt={movie.title} />
<div className="info">
<h3>{movie.title}</h3>
<span>{movie.voteavg}</span>
</div>
<div className="overview">
<h2>Overview</h2>
<p>{movie.overview}</p>
</div>
</div>
}
export default Movie;
This is the home page script.
import NavigationBar from "../NavigationBar/NavigationBar";
import Movie from "../Movie";
import React, { useEffect, useRef, useState } from "react";
const FEATURED_API="baseUrl+apiKey";
const HomePage= () =>{
const movieElement=useRef(new Array());
const [movies,setMovie]=useState([]);
useEffect(()=>{
fetch(FEATURED_API).then((res)=>res.json)
.then(data=>{
setMovie(data.arguments);
});
},[]);
return(
<>
<div className="header">
<header>Welcome to the cinema</header>
<NavigationBar/>
</div>
<div className="movie-container">
{movies.map((movie)=><Movie key={movie} {...movies}/>)}
</div>
</>
);
}
const HomePage= () =>{
const movieElement=useRef(new Array());
const [movies,setMovie]=useState([]);
useEffect(()=>{
fetch(FEATURED_API).then((res)=>res.json)
.then(data=>{
setMovie(data.arguments);
});
},[]);
return(
<>
<div className="header">
<header>Welcome to the cinema</header>
<NavigationBar/>
</div>
<div className="movie-container">
{movies.map((movie)=><Movie key={movie} {...movies}/>)}
</div>
</>
);
}
When your function body has ( ) ie. round(parenthesis) brackets, the return is implicit. ie. whatever is in the curly braces will be returned automatically
const Movie=(movie:movie)=>(
<div className="movie">
<img src={IMG_API+movie.path} alt={movie.title} />
<div className="info">
<h3>{movie.title}</h3>
<span>{movie.voteavg}</span>
</div>
<div className="overview">
<h2>Overview</h2>
<p>{movie.overview}</p>
</div>
</div>
)
When your function body has a { } ie. curly brackets, you have to explicit use the return keyword to return something
const Movie=(movie:movie)=>{
return (
<div className="movie">
<img src={IMG_API+movie.path} alt={movie.title} />
<div className="info">
<h3>{movie.title}</h3>
<span>{movie.voteavg}</span>
</div>
<div className="overview">
<h2>Overview</h2>
<p>{movie.overview}</p>
</div>
</div>
)
}
In the below code, if you have used {}, then you will have to manually return the JSX as :
const Movie=(movie:movie)=>{
return (
<div className="movie">
<img src={IMG_API+movie.path} alt={movie.title} />
<div className="info">
<h3>{movie.title}</h3>
<span>{movie.voteavg}</span>
</div>
<div className="overview">
<h2>Overview</h2>
<p>{movie.overview}</p>
</div>
</div>
)
}
This is what it is complaining about, if you were not using {} then it would have returned it auto using ES6 short form of arrow functions.

How can I display the data of an array from an API?

i'm having a problem displaying the data from the punkbeer API and would appreciate any kind of help
import { useEffect, useState, Fragment } from 'react';
import { Switch, Link, Route, BrowserRouter as Router } from 'react-router-dom';
import React from 'react'
import '../Drink/drinks.css';
function Drinks() {
const [beer, setBeer] = useState('[]');
const [selectedBeer, setSelectedBeer] = useState("")
const fetchDrinks = function() {
fetch(`https://api.punkapi.com/v2/beers`)
.then( function(result) {
return result.json()
})
.then(function (data) {
setBeer(data)
data.forEach(beer => {
<div>
<div className="drink-img">
<img src="{beer.image_url}"/>
</div>
<div className="drink-title">
<h2>{beer.name}</h2> </div>
<div className="drink-tagline">
<h2>{beer.tagline}</h2> </div>
<div className="drink-food">
<h2>{beer.food_pairing}</h2> </div>
</div>
});
})
.catch(function () {
});
}
useEffect( function(){
fetchDrinks();
}, []);
return (
<div className="drink-container">
<div className="next">
<p>After you are picked your drink, click next to place your order </p>
<Link to="./Order">
<button className="btn" >Order</button>
</Link>
</div>
</div>
)
}
export default Drinks
You can save the return in variable/state.
Example you save in variabe that have name "data"
in view, you can add this
{data.map((value,index)=>{
return(
<div key={index}>
<div className="drink-img">
<img src="{value.image_url}"/>
</div>
<div className="drink-title">
<h2>{value.name}</h2> </div>
<div className="drink-tagline">
<h2>{value.tagline}</h2> </div>
<div className="drink-food">
<h2>{value.food_pairing}</h2> </div>
</div>
)}
)}

How to scroll to id after clicking on the internal Link in Gatsby.js

I am building a blog and after clicking on Link linking to an article I want the new page to be scrolled on the Title of the article.
I have tried to use <Link to={`/${node.slug}#blog-title`}>{node.title}</Link>
but it doesn't scroll to title, it scrolls back to top of the page.
I have tried to use window.scrollTo(document.getElementById('blog-title); in componentDidMount but it looks like it's not renered until then and it still goes to top. If I use setTimeout it only scrolls to top to straight away scroll to the correct id.
index.js
return (
<article className="blog-listing" key={node.slug}>
<div className="entry-meta-content">
<h2 className="entry-title">
<Link to={`/${node.slug}`}>{node.title}</Link>
</h2>
<span className="entry-meta">
Created on {node.publishDate} By {node.author.name}
</span>
</div>
<div className="entry-media">
<Img fluid={node.heroImage.fluid} backgroundColor="#f4f8fb" />
</div>
<div className="entry-content-bottom">
<p className="entry-content">{node.body.childMarkdownRemark.excerpt}</p>
<Link to={`/${node.slug}`} className="entry-read-more">
<span />
Read More
</Link>
</div>
</article>
);
blog-post.js
import React, { Component } from 'react';
import get from 'lodash/get';
import Helmet from 'react-helmet';
import { graphql } from 'gatsby';
import Img from 'gatsby-image';
import Template from '../components/layout';
class BlogPostTemplate extends Component {
render() {
const post = get(this, 'props.data.contentfulBlogPost');
return (
<Template>
<Helmet title={`${post.title}`} id="blog-title" />
<div className="inner-blog-post">
<div className="container">
<div className="row">
<div className="col-lg-12 col-md-12">
<div className="entry-media">
<Img fluid={post.heroImage.fluid} backgroundColor="#f4f8fb" />
</div>
<h1 className="section-headline"> {post.title} </h1>
<p> {post.publishDate} </p>
<div
dangerouslySetInnerHTML={{
__html: post.body.childMarkdownRemark.html
}}
/>
</div>
</div>
</div>
</div>
</Template>
);
}
}
export default BlogPostTemplate;
export const pageQuery = graphql`
query blogPostQuery($slug: String) {
contentfulBlogPost(slug: { eq: $slug }) {
title
body {
childMarkdownRemark {
html
}
}
heroImage {
file {
url
}
fluid(maxWidth: 1800) {
...GatsbyContentfulFluid_withWebp_noBase64
}
}
}
}
`;
Thanks!
In your blogpost.js, you're attaching an id to the <Helmet> tag, which doesn't generate any content in the body. Perhaps you should attach it to the title in the body instead?
...
<Template>
- <Helmet title={`${post.title}`} id="blog-title" />
+ <Helmet title={`${post.title}`} />
<div className="inner-blog-post">
<div className="container">
<div className="row">
<div className="col-lg-12 col-md-12">
<div className="entry-media">
<Img fluid={post.heroImage.fluid} backgroundColor="#f4f8fb" />
</div>
- <h1 className="section-headline"> {post.title} </h1>
+ <h1 id="blog-title" className="section-headline"> {post.title} </h1>
...

Resources