Fetch in NextJS doesn't work. What should I do? - node.js

I try to use getStaticProps from NextJs for SSR, but there is always a problem with fetch. When I call fetch inside getStaticProps and try to render data it sends me a 505 Internal Server Error. While when I try to use useState and useEffect, fetch inside useEffect work correctly. I need to create SSR app, so I dont know what to do
enter image description here
const Users = ({ users }) => {
return (
<MainContainer keywords={"users next js"}>
<h1>Cписок пользователей</h1>
<ul>
{users.map((user) => (
<li key={user.id}>
<Link href={`/users/${user.id}`}>
<a>{user.name}</a>
</Link>
</li>
))}
</ul>
</MainContainer>
);
};
export default Users;
export async function getStaticProps(context) {
const response = await fetch(`https://jsonplaceholder.typicode.com/users`);
const users = await response.json();
return {
props: { users }, // will be passed to the page component as props
};
}

Related

NEXT.js Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource

Hi guys I've created fullstack app with NEXT.js, TypeScript, NODE.js and MongoDB. Generally this is decode\encode app with all functionality on Backend side. My backend side is already deployed on heroku. Front-end I still have on a localhost till I solve this problem. When I fetch all of my data from Backend, first I see error mesage, then data from Backend.
Here below error msg:
And here below my code:
import { ListProps } from "./List.props";
import styles from "./List.module.css";
import { P } from '../'
import React, { useEffect, useState } from "react";
import axios from "axios";
export const List = ({ children, className, ...props }: ListProps): JSX.Element => {
const [blogs, setBlogs] = useState<any[]>([]);
useEffect(() => {
const fetchData = async () => {
await fetch(`https://node-test-mongo.herokuapp.com/api/blog`)
.then((response) => {
return response.json();
})
.then((data) => {
setBlogs(data.blogs)
})
};
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [blogs]);
return (
<div
className={styles.ul}
{...props}
>
{blogs && blogs.map((blog, index) => (
<ul key={blog._id}>
<li className={styles.li}>
<P className={styles.title} size='l'>{blog.title}</P>
<P size='l'>description={blog.text} </P>
</li>
</ul>
))}
</div>
)
};
I suppose, that there could be something wrong with my data fetching, but I'm not sure.
Thank you in advance!:)

React - How to update my component when there is a change on the server

I want to reload the page when a put request is successful but it never reloads the page. what is the problem
async function saveEdit() {
await Axios.put('http://localhost:5000/edit', {id: id, newTitle: title, newDescription: description, newImageURL: imageURL});
window.location.reload();
}
the request works but the reload() line doesn't seem to work. or is there a better way to do this?
A simple implementation of useState() hook in react
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [data, setData] = useState(null);
async function saveEdit(id, title, description, imageURL) {
// Make sure that your request responds back with the relevant and fresh batch of data after the update
var res = await Axios.put('http://localhost:5000/edit', {id: id, newTitle: title, newDescription: description, newImageURL: imageURL});
//window.location.href = window.location.href;
var data = res.data;
setData(data)
}
return (
<div>
{data?.map(d=>{
// How you want to processes your data object.
return <h1>d.title</h1>
})}
<button onClick={()=>saveEdit(1,"Mytitle","description","test Url")}> Update </button>
</div>
);
}
For more information in react hooks refer here.

NextJS component

I need to reload a remote JSON every 30 seconds. I currently do it this way in reactJS but since moving to NextJS it does not work
The issue is that the following work fine in my current ReactJS website but as soon as I Moved it to NextJS it printing our errors everywhere.
Mainly with the following
fetchTimeout
sessionStorage
export default function MediaControlCard(props) {
const fetchTimeout = (url, ms, { signal, ...options } = {}) => {
const controller = new AbortController();
const promise = fetch(url, { signal: controller.signal, ...options });
if (signal) signal.addEventListener("abort", () => controller.abort());
const timeout = setTimeout(() => controller.abort(), ms);
return promise.finally(() => clearTimeout(timeout));
};
const controller = new AbortController();
const podcast = props.podcast;
const classes = useStyles();
var token = uuidv4();
// alert(sessionStorage['uuid']);
if(!sessionStorage['uuid']){
sessionStorage.setItem("uuid",token);
}
if(!sessionStorage['station']){
sessionStorage.setItem("station","DRN1");
}
if(!sessionStorage['live']){
sessionStorage.setItem("live",true);
}
var icyStream = "https://api.drn1.com.au:9000/station/"+sessionStorage.station+"?uuid="+sessionStorage['uuid'];
var streamurl = icyStream;//window.com_adswizz_synchro_decorateUrl(icyStream);
React.useEffect(() => {
nowplaying();
document.getElementById("player").muted = false;
});
if(podcast){
alert('test');
}
/*if(!sessionStorage.getItem("station")){
sessionStorage.setItem("station","DRN1");
}*/
function nowplaying(){
// alert("hello");
if(sessionStorage.live === true){
document.getElementById("podcast-only").style.display='none';
}
fetchTimeout(`https://api.drn1.com.au:9000/nowplaying/`+sessionStorage.station+`?uuid=`+sessionStorage['uuid'], 3000, { signal: controller.signal })
.then(res => res.json())
.then(
(result) => {
//console.log("testing player"+result.data);
if(sessionStorage.getItem("live") === 'true'){
switch(result.data[0].track.songtype)
{
case "A":
AdSystem(result.data[0]);
break;
case "S":
Song(result.data[0]);
document.getElementById("Now_Playing_Artist").innerHTML = result.data[0].track.artist;
document.getElementById("Now_Playing_Title").innerHTML = result.data[0].track.title;
document.getElementById("Now_Playing_Cover").style.backgroundImage = "url('"+result.data[0].track.imageurl+"')";
break;
default:
Song(result.data[0]);
document.getElementById("Now_Playing_Artist").innerHTML = result.data[0].track.artist;
document.getElementById("Now_Playing_Title").innerHTML = result.data[0].track.title;
document.getElementById("Now_Playing_Cover").style.backgroundImage = "url('"+result.data[0].track.imageurl+"')";
break;
}
fetch(`https://itunes.apple.com/search?term=${result.data[0].track[0].artist}+${result.data[0].track[0].title}&limit=1`)
.then(res => res.json())
.then(
(result) => {
if(result.results[0]){
document.getElementById("buylink").href = result.results[0].collectionViewUrl;
document.getElementById("buynow").style.display = "block";
}
else
{
document.getElementById("buynow").style.display = "none";
}
})
}
})
.then(console.log)
.catch(error => {
console.error(error);
if (error.name === "AbortError") {
// fetch aborted either due to timeout or due to user clicking the cancel button
} else {
// network error or json parsing error
}
});
setTimeout(function(){nowplaying()}, 10000);
}
return (<>
<Card id="nowplayinginfo_card" className={classes.card}>
<CardMedia
id="Now_Playing_Cover"
className={classes.cover}
image="//tvos.adstichr.com/client/resources/images/stations/Indie/DRN1-Logo.png"
title="Live from space album cover"
/>
<div className={classes.details} id="adstichrNP">
<CardContent className={classes.content} id="song">
<Typography variant="subtitle1">
Now Playing
</Typography>
<Typography id="Now_Playing_Title" component="h6" variant="h6">
{props.artist}
</Typography>
<Typography id="Now_Playing_Artist" variant="subtitle1" color="textSecondary">
{props.song}
</Typography>
</CardContent>
<div id="buynow" className={classes.buynow}>
<a id="buylink" target="_blank" href="#Blank"><img alt="buynow" src="https://linkmaker.itunes.apple.com/assets/shared/badges/en-us/music-lrg-1c05919c6feae5d4731d4399cd656cd72e1fadc4b86d4bd7dc93cb8f3227cb40.svg"/></a>
</div>
<div id="podcast-only" className={classes.controls}>
<audio id="player" className={classes.player} controls controlsList="nodownload" autoPlay muted>
<source src={streamurl}
type="audio/mpeg"
/>
</audio>
</div>
</div>
</Card>
<Card className={classes.card} id="adbanner">
<CardContent className={classes.content} id="adstichr">
</CardContent>
</Card>
</>
)
}
How do I ac achieve this with NextJS. I thought anything I put into component with nextjs would just work the same as ReactJS - clearly not.
NextJS has server-side rendering features for your concern. I believe that you should use getStaticProps there is a special property in it called revalidate it will allow you to make requests on every timeout you wish to use. I took an example from official documentation of latest nextjs(version 11.0)
Docs: https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
export default Blog
You are not showing the errors but I suspect it is related to the server-side rendering feature of next.js.
document is defined only on the browser and since useEffect gets executed only on the browser you are calling nowPlaying inside the useEffect. That is the right thing. However sessionStorage (whatever is the package is) also has to be called on the browser.
You should be always retrieving the data from the storage inside useEffect, before component renders.
Yes, you can't achieve this with proper NextJS. I am using useSWR library, it has some "update" intervals as an option.
You can check it here. ("options" part)
you can use getServerSideProps. Make sure its a page component. getServerSideProps, getStaticProps only works in page component.
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page

API call in render method in React inside map

I have a userId array and I need to show the list of names related to that array. I want to call API call inside the render method and get the username. But this is not working. How can I fix this issue?
Below is my render method:
render(){
...
return(
<div>
{this.state.users.map(userId => {
return (
<div> {this.renderName(userId )} </div>
)
})}
</div>
)
...
}
Below is the renderName function:
renderName = (userId) => {
axios.get(backendURI.url + '/users/getUserName/' + userId)
.then(res => <div>{res.data.name}</div>)
}
Basically you cannot use asynchronous calls inside a render because they return a Promise which is not valid JSX. Rather use componentDidMount and setState to update the users array with their names.
Generally, you do not change state or fetch data in the render method directly. State is always changed by actions/events (clicks, input or whatever). The render method is called everytime a prop/state changes. If you change the state within the render method directly, you end up having an infinite loop.
You should use the lifecycle methods or hooks to load data from an api. Here's an example from the official React FAQ: https://reactjs.org/docs/faq-ajax.html
This will not render anything as the API calls are asynchronous and since renderName function isn't returning anything, it'll return undefined.
You should create a function, which will call api for all the userIds and update in state
getNames = () => {
const promises = [];
this.state.users.forEach((userId) => {
promises.push(axios.get(backendURI.url+'/users/getUserName/'+userId));
})
// Once all promises are resolved, update the state
Promise.all(promises).then((responses) => {
const names = responses.map((response) => response.data.names);
this.setState({names});
})
}
Now you can call this function in either componentDidMount or componentDidUpdate, whenever users data is available.
And finally, you can iterate over names directly and render them
<div>
{this.state.names.map((name) => {
return <div> {name} </div>;
})}
</div>
You could make user name it's own component:
const request = (id) =>
new Promise((resolve) =>
setTimeout(resolve(`id is:${id}`), 2000)
);
const UserName = React.memo(function User({ userId }) {
const [name, setName] = React.useState('');
React.useEffect(() => {
//make the request and set local state to the result
request(userId).then((result) => setName(result));
}, [userId]);
return <div> {name} </div>;
});
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [1, 2],
};
}
render() {
return (
<ul>
{this.state.users.map((userId) => (
<UserName key={userId} userId={userId} />
))}
</ul>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
export default ()=> {
let [users,setUsers] = useState([]);
useEffect(()=>{
let fetchUsersInfoRemote = Promise.all([...Array(10)].map(async (_,index)=>{
try {
let response = await axios.get(`https://jsonplaceholder.typicode.com/posts/${index+1}`);
return response.data;
}
catch(error) {
return ;
}
}));
fetchUsersInfoRemote.then(data=> setUsers(data));
},[]);
return (
<div className="App">
<ul>
{
users.map(user=>(<li><pre>{JSON.stringify(user,null,2)}</pre></li>))
}
</ul>
</div>
);
}

React: How to update the DOM with API results

My goal is to take the response from the Google API perspective and display the value into a div within the DOM.
Following a tutorial : https://medium.com/swlh/combat-toxicity-online-with-the-perspective-api-and-react-f090f1727374
Form is completed and works. I can see my response in the console. I can even store the response into an object, array, or simply extract the values.
The issue is I am struggling to write the values to the DOM even though I have it saved..
In my class is where I handle all the API work
class App extends React.Component {
handleSubmit = comment => {
axios
.post(PERSPECTIVE_API_URL, {
comment: {
text: comment
},
languages: ["en"],
requestedAttributes: {
TOXICITY: {},
INSULT: {},
FLIRTATION: {},
THREAT: {}
}
})
.then(res => {
myResponse= res.data; //redundant
apiResponse.push(myResponse);//pushed api response into an object array
console.log(res.data); //json response
console.log(apiResponse);
PrintRes(); //save the values for the API for later use
})
.catch(() => {
// The perspective request failed, put some defensive logic here!
});
};
render() {
const {flirty,insulting,threatening,toxic}=this.props
console.log(flirty); //returns undefined, makes sense upon initialization but does not update after PrintRes()
return (
<div className="App">
<h1>Please leave a comment </h1>
<CommentForm onSubmit={this.handleSubmit} />
</div>
);
}
}
When I receive a response from the API I use my own function to store the data, for use later, the intention being to write the results into a div for my page
export const PrintRes=() =>{
// apiResponse.forEach(parseToxins);
// myResponse=JSON.stringify(myResponse);
for (var i = 0; i < apiResponse.length; i++) {
a=apiResponse[i].attributeScores.FLIRTATION.summaryScore.value;
b=apiResponse[i].attributeScores.INSULT.summaryScore.value;
c=apiResponse[i].attributeScores.THREAT.summaryScore.value;
d=apiResponse[i].attributeScores.TOXICITY.summaryScore.value;
}
console.log("hell0");//did this function run
// render(){ cant enclose the return in the render() because I get an error on the { , not sure why
return(
<section>
<div>
<p>
Your comment is:
Flirty: {flirty}
</p>
</div>
<div>
<p>
Your comment is:
insulting: {insulting}
</p>
</div>
<div>
<p>
Your comment is:
threatening: {threatening}
</p>
</div>
<div>
<p>
Your comment is:
toxic: {toxic}
</p>
</div>
</section>
);
}
Variables and imports at the top
import React from "react";
//needed to make a POST request to the API
import axios from "axios";
import CommentForm from "../components/CommentForm";
var myResponse;
var apiResponse= [];
let a,b,c,d;
let moods = {
flirty: a,
insulting:b,
threatening:c,
toxic:d
}
If I understand correctly You need to create a state where you store data from api.
States in react works like realtime stores to refresh DOM when something change. this is an example to use it
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
apiData: undefined
};
}
fetchData() {
this.setState({
apiData: "Set result"
});
}
render() {
const { apiData } = this.state;
return (
<div>
<button onClick={this.fetchData.bind(this)}>FetchData</button>
<h3>Result</h3>
<p>{apiData || "Nothing yet"}</p>
</div>
);
}
}
you can check it here: https://codesandbox.io/s/suspicious-cloud-l1m4x
For more info about states in react look at this:
https://es.reactjs.org/docs/react-component.html#setstate

Resources