getServerSideProps proper usage? - javascript

I have recently been trying to create a web app with NextJS. I know some basics in web development but I was a little lost when using NextJS as I didn't do any React either before.
I've tried fetching data from an API and using this data in my page. I struggled a bit but in the end I got it working with the help of getServerSideProps.
My question is, how could I use getServerSideProps multiple times in my application so that I can fetch many other routes ? I've tried using getServerSideProps in a different file, using its response in a function that I then export as a component and use it so I can "get components of getServerSideProps responses" if it makes sense, but had many different errors when trying to do so.
Could someone explain how it actually works and how I could resolve my issue, and if it doesn't work that way, how could I make it work?
Here's an example using Coinbase's API :
import { useState } from 'react'
import fetch from 'isomorphic-fetch'
export const getServerSideProps = async () => {
const res = await fetch('https://api.coinbase.com/v2/prices/ETH-USD/buy')
const data = await res.json()
return {
props: {
ethprice: data
}
}
};
I then use "ethprice" in my Home function such as :
export default function Home({ ethprice }) {
return (
[page content, divs, text etc...]
{etherprice.data.amount}
Thanks!

getServerSideProps is specific to that particular file, you can't just use it in any way you want.
const Example = (props) => {
return // this is your component
}
export const getStaticProps = async () => {
// this will provide props specifically for 'Example'
}
More than that getStaticProps will only be run once on static page generation and never again, along with fetching the props for that particular component only. So you can't get live data from it, only data required to generate the page (like page title).
You can have a look at getServerSideProps if you're looking for something more dynamic that can fetch props at runtime. After that you can pass those props down to children if you need to.

Related

redux toolkit using older data in slice to minimize calls to server

I'm fairly new to developing web apps, I started learning react + redux toolkit while using Django as a backend framework
to my point,
I was trying to minimize calls to the server by using a useEffect to check if the value of a specified selector is filled with data, so then I can use that data instead of calling the server again
now when I make the check
useEffect(() => {
flights.value.length <= 0 && dispatch(fetchFlights())
// eslint-disable-next-line
}, [])
it works when you first call the component
but after that, every time I open that component (whether I click on its link, or using a navigate(-1) to go back to that component) it won't display anything. I'll need to manually refresh the page for it to work correctly
this is for the component to render the data via a map function (works as it displays it when first calling it)
{!logged ? <Login /> : flights.loading ? <div>loading..</div> : flights.value.length > 0 && flights.value.map(...)}
now if i change the useEffect to this:
useEffect(() => {
dispatch(fetchFlights())
// eslint-disable-next-line
}, [])
basically without the data check, it works just fine
I was wondering if there is a way to check for the data and have it displayed without a call to the server again
or hear your thoughts about calling the server again and again and maybe its just better that way?
If you are using redux-toolkit, createApi feature is the best option. You can use the fetched data across your app without retrieving it multiple times or refresh the obtained data based on your needs (polling, caching, manual refetching, invalidating it after a certain time... )
// Need to use the React-specific entry point to allow generating React hooks
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react'
// Define a service using a base URL and expected endpoints
export const fligthsApi = createApi({
reducerPath: 'flights',
baseQuery: fetchBaseQuery({ baseUrl: 'https://yourapi.com' }),
endpoints: (builder) => ({
getFlights: builder.query({
query: () => `/yourFlightsPath`,
}),
}),
})
// Export hooks for usage in function components, which are
// auto-generated based on the defined endpoints
export const { useGetFligthsQuery } = fligthsApi
The you can use it in your app like:
export default function App() {
// Even if this component is unmount, flights data will be cached
const { data, error, isLoading } = useGetFligthsQuery()
// render UI based on data and loading state
}
(This is a minimal example, complete working code needs importing the api in your store)

React useEffect with axios

Below is a snippet of code to fetch data from url by axios,
import React, { useState, setEffect, useEffect } from 'react';
import axios from "axios";
import LoadingPage from "./LoadingPage";
import Posts from "./Posts";
const url = "https://api-post*****";
function App() {
const [posts, setPosts] = useState([]);
const fetchPost = async() => {
try {
const response = await axios(url);
return response.data;
} catch (error) {
console.error(error);
}
};
let data = fetchPost();
setPosts(data);
return (
<main>
<div className="title">
<h2> Users Posts </h2>
{posts.length
? <Posts posts={posts} />
: <Loading posts={posts} />
}
</div>
</main>
);
}
export default App;
However, it got the error of
uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite
Question 1: How could this be of too many re-render, there is no loop or something?
To solve this bug, we can use below changes:
const [posts, setPosts] = useState([]);
const fetchPost = async () => {
try {
const response = await axios(url);
setPosts(response.data);
} catch (err) {
console.error(err);
}
};
useEffect(()=> {
fetchPost();
}, [posts])
Question 2: how the useEffect work to avoid too many calls?
Question 3: I always treat react hooks under hood as web socket communications, etc. If that is the case?
When you call setPosts the component will render again, and fetch the data again, at which point you set state with the data forcing a new render which fetches the data...etc.
By using useEffect you can fetch the data, and set state once when the component is first rendered using an empty dependency array.
useEffect(() => {
// Fetch the data
setposts(data);
}, []);
You probably don't want to watch for posts in this useEffect (you can have many) like you're doing in your updated example because you may run into the same issue.
I will only answer number one.
Caveat: the answer is a bit long.
I really hope it will help you to understand a topic that took me a long time to grasp.
Answer one:
To answer this question we should ask our selves two things, a) what is a side effect? and b) how the life cycle of components works?
so, we know that React functional component are pure functions and they should stay that way, you can pass props as parameters and the function will do stuff and return JSX, so far so good.
and for the second part we cannot control React virtual DOM, so the component will render many times during it's lifecycle, so imagine that the virtual DOM decided to check the code and compare between the virtual DOM and the real DOM and in order to do that he will have to check and "run" the code that resides inside that specific component.
the virtual DOM will run the API call, he will find a different result which will cause a new render to that specific component and this process will go on and on as an infinite loop.
when you are using usEffect you can control when this API call will take place and useEffect under the hood makes sure that the this API call ran only one your specific change take place and not the virtual DOM V.S real DOM change.
to summarize, useEffect basically helps you to control the LifeCycle of the component
Please first check your state like this.
useEffect(()=> {
fetchPost();
}, [posts]);

Invalid hook call trying to make an axios get request in react

I'm recreating a simple weather app with react and am having a lot of trouble creating a very simple API call. I've tried a plethora of methods on here, youtube, etc and continuously get errors. The current code I have is:
import Axios from 'axios';
import { Home } from '../components/Home'
import React, {useState, useEffect} from 'react'
var weatherApiRootUrl = 'https://api.openweathermap.org';
var weatherApiKey = 'd91f911bcf2c0f925fb6535547a5ddc9';
//function to generate weather URL. fetches lat/lon and creates new url
function GetWeatherUrl(loc){
const [weatherLink, setWeatherLink] = useState("");
const getCoordinates = () => {
Axios.get(`${weatherApiRootUrl}/geo/1.0/direct?q=${loc}&limit=5&appid=${weatherApiKey}`).then(
(response)=>{
console.log(response);
setWeatherLink(`${weatherApiRootUrl}/data/2.5/onecall?lat=${response.data.lat}&lon=${response.data.lon}&units=imperial&exclude=minutely,hourly&appid=${weatherApiKey}`);
}
);
};
return(
<div>
{weatherLink}
</div>
);
}
export{
GetWeatherUrl
}
The purpose of this file is just to use the user input (a city name designed by variable loc) to fetch lat/lon coordinates of the inputted city as that data is needed for the second link that fetches weather information. The user input is handled from another component and works fine.
All I intend for this code to do is fetch latitude and longitude data using loc and use those numbers to generate the new link that fetches weather data. The weather data will be done with a different api call on another file. The working method of this current file should be useable for the future file meant to fetch weather so this is kind of two birds in one stone.
I need the weatherLink generated to be able to be exported to do this so I would really prefer a solution allowing that please. I originally was going to just export the raw lat/lon data and use them for the call in the new file as how I did with loc here but I decided returning a completed link as a string would maybe be easier. Really would appreciate someone's help on this it's been frustrating me for way longer than it should!
Not really needed but here's the non-react version of the site if it helps: https://giovannimalcolm.github.io/weather-dashboard/
This can be easily solved without needing to use React at all, since it is not a component and doesn't have any rendering logic in itself per se:
import Axios from 'axios';
const weatherApiRootUrl = 'https://api.openweathermap.org';
const weatherApiKey = 'd91f911bcf2c0f925fb6535547a5ddc9';
export async function GetWeatherUrl(loc) {
const response = await Axios.get(`${weatherApiRootUrl}/geo/1.0/direct?q=${loc}&limit=5&appid=${weatherApiKey}`);
return `${weatherApiRootUrl}/data/2.5/onecall?lat=${response.data.lat}&lon=${response.data.lon}&units=imperial&exclude=minutely,hourly&appid=${weatherApiKey}`;
}
Then in places where you need to use the weather URL, you need to remember that GetWeatherUrl returns a promise, so you've got to await it (or handle it like any other promise):
const myFn = async () => {
const weatherUrl = await GetWeatherUrl(loc);
const weatherUrlData = await Axios.get(weatherUrl);
};
myFn();

I use next.js and i try to do a single post page

i use nextjs and i fetch some dummy data on home and now i want to make a page for each post that i got on home i made it but i feel is not a good code so if someone can healp me clear some things and also learn more on next i will appreciate this a lot. here is the code:
import { useRouter } from 'next/router'
import {useEffect} from 'react'
const post = () => {
const router = useRouter()
const pid = router.query.pid
async function getdata (){
if(pid){
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${pid}`)
const data = await res.json()
console.log(data)
}
}
useEffect(() => {
getdata()
}, [pid])
return(
<p>wasd{pid}</p>
)
}
export default post
I suppose you're talking about making components or each different route for each different data.
FIRST CASE-If we talk about the first case you can have a folder called components outside your pages for folder and make components there and map that component depending on your data or whatever.
SECOND CASE-If you want to have a different route [filename].js is the way to go this represents lets say localhost:3000/foldername/:filename. So you can do stuff from here. I'd suggest reading nextjs docs for more info on this routing.

Nextjs getInitialProps blocked the page rendering in client side?

Since I like to add SSR to my upcoming project to improve SEO, I would like to try out next. What I want is that only use SSR for the initial page, and the rest of navigations in the site will be client side rendering. I see the getInitialProps fit the most in this case, accordingly the documentations.
As my understanding, getInitialProps is run in server for the initial page rendering, and is run in the browser when navigating using next/link. The issue I found is that the getInitialProps seems to block the page rendering. (i.e. page changed/rendered after getInitialProps is completed)
import axios from 'axios'
function Posts(props) {
return (
<div>
<div>Posts:</div>
<div>
{JSON.stringify(props)}
</div>
</div>
)
}
Posts.getInitialProps = async (context) => {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
// Wait longer to see the effect
// await (new Promise((resolve) => {
// setTimeout(resolve, 5000)
// }))
return {
props: {
posts: response.data
}
}
}
export default Posts;
How can I do it like in pure React, render the jsx first, then fill in the props? (the execution JSON.stringify(props) might be ignored at first)
Also, in next 9.3, the team introduced getServerSideProps, which is recommended over getInitialProps. How can they be comparable when they are not the same that getServerSideProps will on run in server?
Based on your comments, you want to do the fetch on the server, on the initial page load. However, if navigating between pages you don't want rendering to block while waiting for getInitialProps to return.
One solution is to check if you're on the server, and do the fetch in getInitialProps. If on the client, don't do the fetch in getInitialProps and instead fetch using useEffect in your render method.
import {useEffect} from 'react'
import axios from 'axios'
const isServer = () => typeof window === 'undefined'
const getPosts = () => {
return axios.get('https://jsonplaceholder.typicode.com/posts')
.then(response => response.data)
}
function Posts({posts}) {
const [renderPosts, setRenderPosts] = useState(posts)
useEffect(() => {
if(posts === null) {
getPosts()
.then(setRenderPosts)
}
}, [])
return (
<div>
<div>Posts:</div>
<div>
{JSON.stringify(renderPosts)}
</div>
</div>
)
}
Posts.getInitialProps = async (context) => {
if(isServer()) {
return {
posts: await getPosts(),
}
}
else {
return {
posts: null,
}
}
}
export default Posts
By the way, you may be tempted to use getServerSideProps here, since it is only called if rendering on the server. However, when a page using getServerSideProps is rendered, it will actually make a call to the server to get data from getServerSideProps, even if you're navigating using next/link. From the Next.js 9.3 blog post:
When navigating between pages using next/link instead of executing getServerSideProps in the browser Next.js will do a fetch to the server which will return the result of calling getServerSideProps.
This would still cause the blocking issue you're wanting to avoid.
One final note, this might not be an idiomatic solution. There may be a more "standard" solution. I just wasn't able to find one. You could likely also use a wrapper around your page component that could do all of this in a more consistent way. If you use this pattern a lot, I'd recommend that.

Categories

Resources