Hybrid rendering (Server + Client Side) - javascript

I want to create an e-commerce platform that requires pages rendered on the server (initial state computed on the server) but with react or vue components for filtering that change the initial state by fetching the filtered data using api calls.
Do you know any javascript framework that support combining server and client side rendering in one route/page ?

Next JS can make this happen. It supports both server and client side rendering.
Here is an example that combines both :
import { useState } from 'react'
import { useRouter } from 'next/router'
function EventList({ eventList }) {
const [events, setEvents] = useState(eventList)
const router = useRouter()
const fetchSportsEvents = async () => {
const response = await fetch('http://localhost:4000/events?category=sports')
const data = await response.json()
setEvents(data)
router.push('/events?category=sports', undefined, { shallow: true })
}
return (
<>
<button onClick={fetchSportsEvents}>Sports Events</button>
<h1>List of events</h1>
{events.map(event => {
return (
<div key={event.id}>
<h2>
{event.id} {event.title} {event.date} | {event.category}
</h2>
<p>{event.description}</p>
<hr />
</div>
)
})}
</>
)
}
export default EventList
export async function getServerSideProps(context) {
const { query } = context
const { category } = query
const queryString = category ? 'category=sports' : ''
const response = await fetch(`http://localhost:4000/events?${queryString}`)
const data = await response.json()
return {
props: {
eventList: data
}
}
}

Related

Failing to fetch dynamic data from firestore using getStaticPaths in nextjs

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;

How to pass the data input from one component into another component?

Introducing The Problem
I am beginner ReactJS learner developing a simple weather app using OpenWeather API. The app is designed to fetch data from two components: one that returns the current weather of the user input and another one that returns the weather forecast for the next 5 days.
When the city name is typed down into the input field, the following message appears on the console:
GET https://api.openweathermap.org/data/2.5/weather?q=undefined&units=metric&appid=${Api.key} 400 (Bad Request)
I do not know how to pass the data from Search Component into App Component. Seriously, I have tried a lot of alternatives but they have been unsuccessful. There are commented lines of code to show my last try so far.
(ignore ForecastWeather because this component is empty)
I know that all of you are quite busy folks, but I would appreciate the help in a respectful way. Even suggestions about what I have to study (e.g. callBack) are welcome. I've tried this already:
https://stackoverflow.com/questions/56943427/whether-to-save-form-input-to-state-in-onchange-or-onsubmit-in-react
https://sebhastian.com/react-onchange/
The code is forward below:
App.js
import React, { useState } from "react";
import { Api } from "./Api";
import {
Search,
CurrentWeather,
ForecastWeather,
Footer,
} from "./components/index";
import "./App.css";
function App() {
const [getCity, setGetCity] = useState();
const [weatherData, setWeatherData] = useState(null);
const [forecastData, setForecastData] = useState(null);
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${getCity}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${getCity}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setGetCity(dataSearch);
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
return (
<div className="App">
<Search
searchResultData={handleSearchLocation}
textPlaceholder="Search for a place..."
/>
{weatherData && <CurrentWeather resultData={weatherData} />}
<ForecastWeather resultData={forecastData} />
<Footer />
</div>
);
}
export default App;
Search.jsx
import React, { useState } from "react";
function Search({ textPlaceholder, searchResultData }) {
const [searchCity, setSearchCity] = useState("");
//const handlerOnChange = ( event, dataSearch ) => {
//setSearchCity(event.target.value);
//setSearchCity(dataSearch);
//searchResultData(dataSearch);
//};
return (
<div className="componentsBoxLayout">
<input
value={searchCity}
//onChange={handlerOnChange}
onChange={(event) => setSearchCity(event.target.value)}
onKeyDown={(event) => event.key === "Enter" && searchResultData(event)}
placeholder={textPlaceholder}
/>
</div>
);
}
export default Search;
CurrentWeather.jsx
import React from "react";
function CurrentWeather({ resultData }) {
return (
<div className="componentsBoxLayout">
<p>{resultData.name}</p>
</div>
);
}
export default CurrentWeather;
ForecastWeather.jsx (empty)
import React from 'react';
function ForecastWeather() {
return (
<div className="componentsBoxLayout">ForecastWeather</div>
)
}
export default ForecastWeather;
Api.js
const Api = {
url: "https://api.openweathermap.org/data/2.5",
key: "etcetc",
img: "https://openweathermap.org/img/wn",
};
export { Api };
Yippee-ki-yay
You can not use getCity in this function:
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${getCity}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${getCity}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setGetCity(dataSearch);
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
getCity is defined on that function so it does not exist when you try to use it, unless you need getCity later for another component I would delete it becuase is redundant and do this:
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${dataSearch}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${dataSearch}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
When you run searchResultData on the search component you send the city you are looking for. Remember that useState will trigger a re-render but a function that is already running before that will never get the new value of the state if the state changes

Is there anyway to pass state to getServerSideProps

I am new to next.js. I want to pass page state to getServerSideProps. Is it possible to do this?
const Discover = (props) => {
const [page, setPage] = useState(1);
const [discoverResults, setDiscoverResults] = useState(props.data.results);
// console.log(discoverResults, page);
return (
<div>
<Card items={discoverResults} render={(discoverResults) => <DiscoverCard results={discoverResults} />} />
</div>
);
};
export default Discover;
export async function getServerSideProps() {
const movieData = await axios.get(`https://api.themoviedb.org/3/discover/movie?api_key=${process.env.NEXT_PUBLIC_MOVIE_DB_KEY}&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=${page}&with_watch_monetization_types=flatrate`);
return {
props: {
data: movieData.data,
},
};
}
the only way is changing your route with params and recive it in server side :
import { useRouter } from "next/router";
const Discover = (props) => {
const { page } = props;
const router = useRouter();
const goToNextPage = () => {
router.replace(`/your-page-pathname?page=${+page + 1}`);
}
return (
<div>
page is : {page}
<button onClick={goToNextPage}>
next page
</button>
</div>
);
};
export default Discover;
export async function getServerSideProps(context) {
const { page } = context.query;
return {
props: {
page: page || 0,
},
};
}
To read more on the topic, recommend reading: Refreshing Server-Side Props
I recommend to use SWR for handling this kind of api calls
an example of this here:
https://swr.vercel.app/examples/ssr
In this example, it can be seen that the api calls happens in the Server side and it is being cached in the Client side.
For handling the query from the urls. This can be done using the same methods as well following the examples from their documentation of SWR https://swr.vercel.app/docs/pagination#pagination
SWR will help alot of stuffs in the api state management. I really recommend to start learning it as soon as possible..

logging the data but not rendering p tag , why?

I am using firebase firestore and i fetched the data , everything is working fine but when i am passing it to some component only one item gets passed but log shows all the elements correctly.
I have just started learning react , any help is appreciated.
import React, { useEffect, useState } from 'react'
import { auth, provider, db } from './firebase';
import DataCard from './DataCard'
function Explore() {
const [equipmentList, setEquipments] = useState([]);
const fetchData = async () => {
const res = db.collection('Available');
const data = await res.get();
data.docs.forEach(item => {
setEquipments([...equipmentList, item.data()]);
})
}
useEffect(() => {
fetchData();
}, [])
equipmentList.forEach(item => {
//console.log(item.description);
})
const dataJSX =
<>
{
equipmentList.map(eq => (
<div key={eq.uid}>
{console.log(eq.equipment)}
<p>{eq.equipment}</p>
</div>
))
}
</>
return (
<>
{dataJSX}
</>
)
}
export default Explore
You have problems with setting fetched data into the state.
You need to call setEquipments once when data is prepared because you always erase it with an initial array plus an item from forEach.
The right code for setting equipment is
const fetchData = async () => {
const res = db.collection('Available');
const data = await res.get();
setEquipments(data.docs.map(item => item.data()))
}

React Query with server side rendering using Next.js

I am trying to use react-query with nextjs to prefetch query on server. It works for the initial query which gets a list of items. However when I try to fetch each item inside component it only fetches it on the client side.
export default function Home() {
const { data } = useQuery("pokemons", fetchPokemons);
return (
<>
<div>
{data.map((pokemon) => (
<Pokemon key={pokemon.name} pokemon={pokemon}/>
))}
</div>
</>
);
}
export async function getStaticProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery('pokemons', fetchPokemons)
const fetchedPokemons = queryClient.getQueryData()
//query each pokemon
fetchedPokemons.forEach(async (pokemon) => {
await queryClient.prefetchQuery(pokemon.name, () => fetchPokemon(pokemon.url))
});
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
And here is code for the component which also queries each item.
const Pokemon = ({pokemon}) => {
const {data} = useQuery(pokemon.name, () => fetchPokemon(pokemon.url))
// logs only in browser, on server it is undefined
{console.log(data)}
return (
<div>
<h3>
Name - {data.name}
</h3>
<h4>Base XP - {data.base_experience}</h4>
</div>
)
}
Can you please tell me what am I doing wrong that the query doesn't execute on server or is it an issue of the library itself?
when you use getQueryData to get data from the cache, you need to provide the key of the data you want to get:
await queryClient.prefetchQuery('pokemons', fetchPokemons)
const fetchedPokemons = queryClient.getQueryData('pokemons')
alternatively, you can use fetchQuery to also retrieve the data immediately
try {
const fetchedPokemons = await queryClient.fetchQuery('pokemons')
} catch (error) {
// handle error
}
Be aware that fetchQuery throws errors (as opposed to prefetchQuery, which does not), so you might want to handle errors somehow.
I was able to solve this by combining two of my fetching functions into one like so
const fetchPokemons = async () => {
const { data } = await axios.get(
"https://pokeapi.co/api/v2/pokemon?limit=10&offset=0"
);
const pokemonArray = await Promise.all(
data.results.map(async (pokemon) => {
const res = await axios.get(pokemon.url);
return res.data;
})
);
return pokemonArray;
};
export default function Home() {
const { data } = useQuery("pokemons", fetchPokemons);
return (
<>
<div>
{data.map((pokemon) => (
<Pokemon key={pokemon.name} pokemon={pokemon}/>
))}
</div>
</>
);
}
export async function getStaticProps() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery("pokemons", fetchPokemons);
return {
props: {
dehydratedState: dehydrate(queryClient),
},
};
}

Categories

Resources