Problem getting API images to display in NextJS App - javascript

This is my first time using NextJS and I'm trying to load 3 random dog breed images onto the app's webpage using the Dog.ceo API. I am able to see the three random dogs in the console from the console.log(data) line, but the images aren't being displayed. In this API there are only two properties - message (containing the image URL) and status (displaying 'success'). Any help in how to get these images to display? Also to note, I'm not using Typescript for this.
const defaultEndpoint = "https://dog.ceo/api/breeds/image/random/3";
export async function getServerSideProps() {
const res = await fetch(defaultEndpoint);
const data = await res.json();
return {
props: { data },
};
}
export default function Home({ data }) {
console.log("data", data);
const { results = [] } = data;
return (
<div className={styles.container}>
<Head>
<title>Dog Breed App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<div className="grid">
{results.map((result) => {
const { message } = result;
return (
<div key={message}>
<img src={message} alt=""></img>
</div>
);
})}
</div>
</main>
</div>
);
}
I tried using "message" from the "data" variable to get the url for the image. But that isn't working.

It's just a destructuring error. You have const { results = [] } = data;.
That line says: Find the property in my data object called results and if it doesn't exist, set it to an empty array. Your data object doesn't have a property called results. It has a property called message.
You could change this line to const { message = [] } = data and then just loop over the message array or you could just store the message array in the props.data property like this:
export async function getServerSideProps() {
const res = await fetch('https://dog.ceo/api/breeds/image/random/3');
// Destructure the response object here and
// rename the 'message' property as 'data'
const { message: data } = await res.json();
return {
props: { data },
};
}
// Destructure the props object to have access to the
// property named data:
export default function Home({ data }) {
return (
<main>
<div className="grid">
{data.map((img) => (
<div key={img}>
<img src={img} alt="dog"></img>
</div>
))}
</div>
</main>
);
}

you can use useEffect hook to load data and update to a state dogs. This will update render once on component creation.
const defaultEndpoint = "https://dog.ceo/api/breeds/image/random/3";
import React, { useState, useEffect } from 'react'
export default function Home({ data }) {
const [dogs, setDogs] = useState([]);
export async function getServerSideProps() {
const res = await fetch(defaultEndpoint);
const data = await res.json();
console.log("data", data);
setDogs(data)
}
useEffect(() => {
getServerSideProps()
}, [])
return (
<div className={styles.container}>
<Head>
<title>Dog Breed App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<div className="grid">
{dogs.map((result) => {
const { message } = result;
return (
<div key={message}>
<img src={message} alt=""></img>
</div>
);
})}
</div>
</main>
</div>
);
}
I'll suggest using [dependencies] in useEffect to control when it re-renders it like below
useEffect(() => {
//
return () => {
//
}
}, [dependencies])

Related

How do I target each image seperately, and fetch API data from them, instead of collect it all at once

I've created an app that connects to an API that retrieves dog images. On page load, 12 images are displayed, along with json text, that provides information about the breeds; height of the dog etc.
My final step would be somehow connecting the a button (which already exists) to each individual image, then retrieving data for that one specific dog/image after clicking it, instead of the API fetching all of the data at once on initial page load.
App.js
import './App.css';
import './Dog.js';
import './index.css';
import FetchAPI from './FetchAPI';
function DogApp() {
return (
<div className="dogApp">
<FetchAPI />
</div>
);
}
export default DogApp;
FetchAPI.js
import React, { useState, useEffect } from 'react'
const FetchAPI = () => {
const [data, setData] = useState([]);
const apiGet = () => {
const API_KEY = "";
fetch(`https://api.thedogapi.com/v1/images/search?limit=12&page=10&order=Desc?API_KEY=${API_KEY}`)
.then((response) => response.json())
.then((json) => {
console.log(json);
//setData([...data,json]); if json is single object
setData([...data, ...json]); // if json is array of one object then use this line
});
};
useEffect(() => { //call data when pagee refreshes/initially loads
apiGet();
}, []);
return (
<div>
{data.map((item) => (
<div class="dog">
<img src={item.url}></img>
<button onClick={item.breeds}>Fetch API</button>
</div>
))}
{data.map((item) => (
<p>{JSON.stringify(item.breeds)}</p>
))}
{/*<pre>{JSON.stringify(data, null, 2)}</pre> */}
<br />
</div>
)
}
export default FetchAPI;
Make an other function which will fetch new (single) image and change it to the state as I have made function named apiGetSingle which changes the data on specific index. And if you have made the route as I have mentioned in apiGetSingle which will return single new image then it will work fine otherwise made backend route for that too.
import React, { useState, useEffect } from 'react'
const FetchAPI = () => {
const [data, setData] = useState([]);
const apiGet = () => {
const API_KEY = "";
fetch(`https://api.thedogapi.com/v1/images/search?limit=12&page=10&order=Desc?API_KEY=${API_KEY}`)
.then((response) => response.json())
.then((json) => {
console.log(json);
//setData([...data,json]); if json is single object
setData([...data, ...json]); // if json is array of one object then use this line
});
};
const apiGetSingle = (index) => {
const API_KEY = "";
fetch(`https://api.thedogapi.com/v1/images/search?API_KEY=${API_KEY}`)
.then((response) => response.json())
.then((json) => {
console.log(json);
let d=[...data];
d[index]=json; // if json is single object.
d[index]=json[0] // if json returns array
setData(d);
};
useEffect(() => {
}, []);
return (
<div>
{data.map((item,index) => (
<div class="dog">
<img src={item.url}></img>
<button onClick={()=>apiGetSingle(index)}>Fetch API</button>
</div>
))}
{data.map((item) => (
<p>{JSON.stringify(item.breeds)}</p>
))}
<button onClick={apiGet}>Fetch API</button>
{/*<pre>{JSON.stringify(data, null, 2)}</pre> */}
<br />
</div>
)
}
export default FetchAPI;

getStaticPaths is required for dynamic SSG

I am new to Next.js and
I've been trying to to use getStaticProps in my dynamic pages in my Next.js app
and I get this error:
Error: getStaticPaths is required for dynamic SSG pages and is missing
for '/query/[itmid]'
[itmid].jsx
function Query({ posts }) {
return (
{posts.map((itm, k) => {
return (
<>
<Head>
<title> {itm.Name} - wixten </title>
</Head>
<div key={itm._id} className="Question-one">
<h1> {itm.Name}</h1>
<h2>{itm.Summary}</h2>
</div>
<div className="username">
<span className="username2">--{itm.username}</span>
</div>
</>
);
})}
</>
<div className="multi-container">
<Answershooks id={gotid} />
<RealtedPost />
</div>
</>
);
}
export async function getStaticProps() {
const res = await fetch("https://ask-over.herokuapp.com/questone/" + gotid);
console.log("check");
console.log("dada");
const posts = await res.json();
return {
props: {
posts,
},
};
}
export default Query;
Why am I getting this error?
What getStaticProps does is to generate the static page, but you need to tell next js, what are the paths to generate?
export async function getStaticPaths() {
return {
paths: [
{ params: { query: 'slug-1' }},
{ params: { query: 'slug-2' }}
],
fallback: true // false or 'blocking'
};
}
Then in your getStaticProp
export async function getStaticProps({ params }) {
//params.query will return slug-1 and slug-2
const res = await fetch("https://ask-over.herokuapp.com/questone/" + params.query);
console.log("check");
console.log("dada");
const posts = await res.json();
return {
props: {
posts,
},
};
}
You need to use params.query if you name your file [query].js.
The above codes will generate static paths /slug-1 and /slug-1.
If you are not trying to generate static pages (which seems like it), then you should probably use getServerSideProps which generates page on the go.

React.js display image from saved api response

So I want to do a simple image fetch from API. My goal is to display random image from API. Now it says "Data" is not defined. I have no idea why it does that because my console.logs were working before trying to show it on page.
This is my App.js
import React,{useEffect, useState} from 'react';
import Dog from './doggo';
//Main component
function App() {
const [dogs, setDog] = useState();
useEffect(() => {
getDog();
}, []);
//Function to get data
const getDog = async () => {
//Fetch from url
const response = await fetch("https://dog.ceo/api/breeds/image/random");
//Store answer in data
const data = await response.json();
//Place data.message in setDog
setDog(data.message);
console.log(data.status);
//data.message contains image url
console.log(data.message);
};
return(
<div>
<h1>Press button to see your future dog!</h1>
<button type="submit">See your dog!</button>
<Dog
image={data.message}
/>
</div>
);
};
export default App;
I reformatted you code a bit to take care of some issues.
As other commenters have stated, data is out of scope where you're trying to access it. (It's only available inside of the getDog() function.)
export default function App() {
const [dog, setDog] = useState();
const getDog = async () => {
const response = await fetch("https://dog.ceo/api/breeds/image/random");
const data = await response.json();
setDog(data.message);
};
return (
<div>
<h1>Press button to see your future dog!</h1>
<button
onClick={() => {
getDog();
}}
>
See your dog!
</button>
{dog ? <Dog image={dog} /> : null}
</div>
);
}
Working Codepen
use {dogs} instead of {data.message} in <Dog image={data.message}/> data is a variable only for the getDog() function.

Looping through an object in react

Learning react
Trying to loop through an object from an API call that returns a json object and display it but struggling to implement it
This is the component that should render it
export default class ProfilePage extends Component {
constructor() {
super();
this.state = { data: '' };
}
mapObject(object, callback) {
return Object.keys(object).map(function (key) {
return callback(key, object[key]);
})
}
async componentDidMount() {
const response = await fetch(`https://indapi.kumba.io/webdev/assignment`);
const json = await response.json();
// console.log(json)
this.setState({ data: json });
}
render() {
const data = this.state.data
console.log(data)
return (
<div className="row">
{Object.values(data).map(data => {
<div key={key}>
{data[key]}
</div>
})
}
Woerkkk please
</div>
);
}
}
All I'm getting is a blank screen.
in the console i get the error 'key' is not defined no-undef
You are missing a return statement in your map for your render method.
Edit: Key is not returned from Object.values
Either reconfigure with a return statement like so:
{Object.keys(data).map(key => {
return (<div key={key}>
{data[key]}
</div>);
})
Or alternatively you can implicitly return from arrow function using brackets
{Object.keys(data).map(key => (
<div key={key}>
{data[key]}
</div>)
))
Using Object.values(myObj) you can get all object values as a array. So, with this array, you can iterate over the array and show your items, like this:
{Object.values(myObj).map(value => <p>{value}</p>)}
Don't forget use key prop when iterating.
You can use useState and useEffect to fetch the object data
const App = () => {
const [objData, setObjData] = useState({});
const [objItems, setObjItems] = useState([]);
const fetchObj = async () => {
const response = await fetch(`https://indapi.kumba.io/webdev/assignment`);
const data = await response.json();
setObjData(data);
setObjItems(data.items);
}
useEffect(() => {
fetchObj()
},[]);
return(
<div>
<h1> Order Id :{objData.order_id}</h1>
// or any other objData keys
<h1>Items : </h1>
<ul>
{
objItems.map((i, idx) => {
return(
<li key={idx}>Name : {i.name} , Category: {i.category}, Price: {i.price}, Currency: {i.currency}</li>
)
})
}
</ul>
</div>
)
}
export default App;

Does React state get saved before setting it with useState?

Sorry for the confusing question. First I will like to share my code:
const intialState = {
output: null,
bookIdToDelete: null
}
const [state,setState] = useState(intialState)
useEffect(() => {
getDataFromServer().then(data => {
console.log(data);
if (data != 2) {
// job gonna be here
const output = data.map(book => {
return (
<div key={book._id} className="col-md-3">
<div className="item">
<img className="bookimage" src={book.imgs[0]} alt="img"/>
<h3>
<Link to={"/admin/mybook/" + book._id}>{book.title}</Link>
</h3>
<h6>
<Link to={"/admin/mybook/" + book._id}>Edit</Link>
<button id={book._id} onClick={deleteBtnClick} className="btn btn-danger">Delete</button>
</h6>
</div>
</div>
)
})
setState({...state, output})
}
}).catch(error => {
console.log(error);
})
}, [])
const deleteBtnClick = (e) => {
let bookId = e.target.id;
console.log(state)
setState({
...state,
bookIdToDelete: bookId
})
}
console.log('showmodal',state);
return (
<>
{state.output}
</>
)
}
export default MyBooks
Basically what this does is that I obtain an array with some information and create a list using a map. I do this all inside the useEffect. Every item has a delete button that calls another function that just saves the id of the element that I want to delete inside the state. What is happening is that when I click on this delete button the state is always equal to the initial state and not with the one I updated in the useEffect. It seems that as I created the array of elements inside the useEffect before I set the state to the its new value the state that is used in the "deleteBtnClick" function is the old one, in this case the initial one....
So I solved this by just saving the information in the useEffect and then creating the list with the map before the return of the component... But I still have the doubt of why it was happening... so if you know why I will appreciate the explanation. Thanks!
in general I would organize my code like this:
const intialState = {
output: [],
bookIdToDelete: null
}
const [state,setState] = useState(intialState)
useEffect(() => {
getDataFromServer().then(data => {
console.log(data);
if (data != 2) {
// job gonna be here
const output = data
setState({...state, output})
}
}).catch(error => {
console.log(error);
})
}, [])
const deleteBtnClick = (e) => {
let bookId = e.target.id;
console.log(state)
setState({
...state,
bookIdToDelete: bookId
})
}
return (
<>
{state.output.map(book => {
return (
<div key={book._id} className="col-md-3">
<div className="item">
<img className="bookimage" src={book.imgs[0]} alt="img"/>
<h3>
<Link to={"/admin/mybook/" + book._id}>{book.title}</Link>
</h3>
<h6>
<Link to={"/admin/mybook/" + book._id}>Edit</Link>
<button id={book._id} onClick={deleteBtnClick} className="btn btn- danger">Delete</button>
</h6>
</div>
</div>
)
})}
</>
)
}
export default MyBooks

Categories