I created an ecom store with Next.js and MongoDB as my database. Fetching of data in my localmachine works with using getServerSideProps, but on deployment it doesn't seem to work.
I attached the code
SUMMER FINE PAGE
This page fetches data from MongoDB in the summer fine collection, and it works as the getServerSideProps URL points to localHost.
`import React from 'react'
import Footer from '../component/Footer'
import Header from '../component/Header'
import Link from "next/link"
import axios from 'axios'
import { useRouter } from 'next/router'
export default function SummerFine({ allProducts }) {
const isServerReq = req => !req.url.startsWith('/_next');
const router = useRouter()
return (
<div>
<Header />
<p className=" itemHeader ml-auto text-left"> <span className='cursor-pointer' onClick={() => router.push('/men')}>Men</span> </p>
<p className='itemHeader_Main text-5xl text-left'>SUMMER FINE SELECTON</p>
<div className="grid productSection lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 gap-4 mt-10">
{
allProducts.map((summerFineItem) => (
<Link href={`/summerFineItem/${summerFineItem._id}`} passHref key={summerFineItem._id}>
<div className='productGrid ' key={summerFineItem._id}>
<img className='productImages' src={summerFineItem.images} />
<br />
<p className='itemName'>{summerFineItem.name}</p>
<br />
<p className='itemPrice'> $ {summerFineItem.price}
</p>
</div>
</Link>
))
}
</div>
<Footer />
</div>
)
}
export const getServerSideProps = async () => {
let prodRes = await axios.get("http://localhost:3000/api/products")
return {
props: {
allProducts: prodRes.data
}
}
}
Here is the API for fetching
import dbConnect from "../../../utils/Mongo";
import Product from "../../../model/Product";
export default async function Handler (req, res) {
const {method} = req
// Connect to Database
console.log("Connecting to the database...");
await dbConnect()
console.log("Database Connected");
if (method === "GET") {
const allProducts = await Product.find()
res.status(200).json(allProducts)
}
if (method === "POST") {
try {
const newProduct = await Product.create(req.body);
res.status(200).json(newProduct);
console.log("New Product created");
} catch (error) {
res.status(500).json(error);
}
}
}
the URL for the domain the website is to be hosted is https://lacostestore.vercel.app/
I think the issue is from the getServerSideProps
I want to be able to fetch data stores in MongoDB to the client after being deployed on vercel
Related
This is my newsapp and this my News.js file of component folder
import React, { Component } from 'react'
import NeswItem from './NewsItem'
export class News extends Component {
constructor(){
super();
this.state={
data : null,
loading : false
}
}
componentDidMount(){
let url = //your news api url
fetch(url).then((res)=>{
res.json().then((result)=>{
console.log(result.articles)
this.setState({data:result.articles})
})
})
}
render() {
return (
<>
<div className="container my-3">
<h2>Top Headlines</h2>
<div className='row'>
{this.state.data ?
this.state.data.map((element)=>
<div className="col-md-4" key={element.url} >
<NeswItem title={element.title?.slice(0, 45)} description={element.description?.slice(0, 88)} imgurl={element.urlToImage} newsUrl={element.url}/>
</div>
)
: null
}
</div>
</div>
</>
)
}
}
export default News
I am creating a react app to show latest news and this is my App.js file
import './App.css';
import React, { Component } from 'react'
import Navbar from './components/Navbar';
import News from './components/News';
export default class App extends Component {
render() {
return (
<>
<News/>
</>
)
}
}
and this is my NewsItem.js file from component folder
import React, { Component } from 'react'
export class NeswItem extends Component {
render() {
let {title, description, imgurl,newsUrl}= this.props;
return (
<div>
<div className="card my-3" style={{width: "18rem"}}>
<img src={!imgurl?"https://img.etimg.com/thumb/msid-96022092,width-1070,height-580,imgsize-60058,overlay-economictimes/photo.jpg":imgurl} className="card-img-top" alt="..."/>
<div className="card-body">
<h5 className="card-title">{title}...</h5>
<p className="card-text">{description}... </p>
Read more
</div>
</div>
</div>
)
}
}
export default NeswItem
I want to convert my componentDidMount function into async componentDidMount one but I was unable to do it. note: I am using my api from newsapi.org
Just use async keyword before componentDidMount
async componentDidMount(){
let url = //your news api url
let res = await fetch(url)
let data = await res.json()
//rest of your code
}
You cannot behave componentDidMount asynchronously, and what you did now is ok
if you want to show loading before recieving data you can use your loading state that you already defined
here is sample :
componentDidMount(){
this.setState({loading:true})
let url = //your news api url
fetch(url).then((res)=>{
res.json().then((result)=>{
console.log(result.articles)
this.setState({data:result.articles,loading:false})
})
})
and in jsx :
<div>
{
this.state.loading ? 'loading...' : null
}
</div>
I want to convert my componentDidMount function into async componentDidMount one
If you did that, it would be misleading. React calls componentDidMount and completely ignores any return value it provides. So if you made it async, it would return a promise that wouldn't be used by anything.
Instead, either do what you're doing (but handle errors), or write an async function to do it and call that function (while handling errors).
The first option:
componentDidMount() {
let url = "your news api url";
fetch(url)
.then((res) => {
if (!res.ok) {
throw new Error(`HTTP error ${res.status}`);
}
return res.json();
})
.then(({articles}) => {
console.log(articles);
this.setState({ data: articles });
})
.catch((error) => {
// ...handle/report error...
});
}
The second:
async #getArticles() {
let url = "your news api url";
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP error ${res.status}`);
}
const { articles } = await res.json();
console.log(articles);
this.setState({ data: articles });
}
componentDidMount() {
this.#getArticles().catch((error) => {
// ...handle/report error...
});
}
(I've also added a check of res.ok, since your code was falling prey to the footgun in the fetch API that I describe here. fetch only rejects its promise on network errors, not HTTP errors like 404 or 500.)
I have an issue where my page is trying to render before the data is available. I have async awaits in place, however, the page gets an error saying data is undefined. When I comment out my page elements and check react dev tools I can see the data object in full, so I know the data request is working.
I need to put in a check for the data and if present then render but as a new developer I am not sure how to implement this in my code.
import React, { useEffect, useState } from "react";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { getDoc, doc } from "firebase/firestore";
import { db } from "../api/auth/firebase/config";
import Head from "next/head";
import ArtistHeader from "../../components/ArtistHeader";
import UploadButton from "../../components/UploadButton";
import styles from "../../styles/artistPage.module.css";
export default function Artist() {
const { data: session, status, loading } = useSession();
const [artist, setArtist] = useState();
const router = useRouter();
const artistId = router.query.artistId;
const fetchArtist = async () => {
const artistRef = doc(db, "users", `${artistId}`);
const docSnap = await getDoc(artistRef);
setArtist(docSnap.data());
};
useEffect(() => {
if (!router.isReady) return;
console.log(artistId);
if (status === "unauthenticated") {
router.push("/auth/signin");
}
fetchArtist();
}, [status, loading, router]);
return (
<section className={styles.wrapper}>
<Head>
<title>{artist.screenName}</title>
</Head>
<div className={styles.artistPage}>
<ArtistHeader artist={artist} />
<div className={styles.songContainer}>
<UploadButton />
</div>
</div>
</section>
);
}
Thanks in advance for help.
use optional chaining. This will prevent you from getting undefined error.
see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
On the other hand you can do the following:
const [isLoading, setIsLoading] = useState(false)
const [isError, setIsError] = useState(false)
const fetchArtist = async () => {
setIsLoading(true)
const artistRef = doc(db, "users", `${artistId}`);
try{
const docSnap = await getDoc(artistRef);
setArtist(docSnap.data());
}catch(e){
setIsError(true)
}
setIsLoading(false)
};
if(isLoading && !artist){
return (
<h2>Loading...</h2>
)
}
if(!isLoading && isError){
return (
<h2>Something went wrong</h2>
)
}
return (
<section className={styles.wrapper}>
<Head>
<title>{artist?.screenName}</title>
</Head>
<div className={styles.artistPage}>
<ArtistHeader artist={artist} />
<div className={styles.songContainer}>
<UploadButton />
</div>
</div>
</section>
)
But I would prefer react-query for server state management. It handles all your loading | revalidation | caching and more.
Check out https://tanstack.com/query/v4/docs/adapters/react-query
Let's make it simple with useQuery hook from react-query
import { useQuery } from '#tanstack/react-query'
const fetchArtist = async (artistId: string) => {
const artistRef = doc(db, "users", `${artistId}`);
return getDoc(artistRef);
};
function Artist() {
const query = useQuery(['artist', artistId], fetchArtist)
const {isLoading, isError, data} = query
if(isLoading){
return (
<h2>Loading...</h2>
)
}
if(isError && !data){
return (
<h2>Something went wrong</h2>
)
}
return (
<section className={styles.wrapper}>
<Head>
{/* optional chaining (?.) */}
<title>{data?.artist?.screenName}</title>
</Head>
<div className={styles.artistPage}>
<ArtistHeader artist={data?.artist} />
<div className={styles.songContainer}>
<UploadButton />
</div>
</div>
</section>
)
}
// _app.jsx
import { Hydrate, QueryClient, QueryClientProvider } from '#tanstack/react-query'
export default function MyApp({ Component, pageProps }) {
const [queryClient] = React.useState(() => new QueryClient())
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
)
}
You can use getServerSideProps to call API on the server. Whenever data is ready, the page will start loading on the client-side.
import React, { useEffect, useState } from "react";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { getDoc, doc } from "firebase/firestore";
import { db } from "../api/auth/firebase/config";
import Head from "next/head";
import ArtistHeader from "../../components/ArtistHeader";
import UploadButton from "../../components/UploadButton";
import styles from "../../styles/artistPage.module.css";
export default function Artist({ data }) {
const { data: session, status, loading } = useSession();
const artist = data; //get data from the server
const router = useRouter();
useEffect(() => {
if (status === "unauthenticated") {
router.push("/auth/signin");
}
}, [status, loading, router]);
return (
<section className={styles.wrapper}>
<Head>
<title>{artist.screenName}</title>
</Head>
<div className={styles.artistPage}>
<ArtistHeader artist={artist} />
<div className={styles.songContainer}>
<UploadButton />
</div>
</div>
</section>
);
}
export async function getServerSideProps(context) {
const artistId = context.params.artistId;
const artistRef = doc(db, "users", `${artistId}`);
const docSnap = await getDoc(artistRef);
const data = docSnap.data();
return { props: { data: data || null } }
}
You can use a state isLoading. The initial value of isLoading will be false. Inside useEffect before fetching data set isLoading value as true and after completing fetching set isLoading as false. Now use conditional rendering if isLoading then render a Loader component else render jsx with data.
I am not able to retrieve content from API every time I reload my page it shows error, please see the attached image, I wanted to find the weather details using Weather API and right now I am using static latitude and longitude.
import React, { useState, useEffect } from "react";
import axios from "axios";
import { FaRegSun } from "react-icons/fa";
import "./stylesheets/stylesheets.css";
function WeatherApp1() {
const [weatherData2, setWeatherData2] = useState({});
const API_endpoint2 = `https://api.openweathermap.org/data/2.5/onecall?`;
const API_key = `2a63c27d8ba0b0d14c9e5d59f39ee1ba`;
useEffect(() => {
async function getSecondObject() {
const response = await axios.get(
`${API_endpoint2}lat=28.4360704&lon=77.021184&units=metric&appid=${API_key}`
);
setWeatherData2(response.data);
}
getSecondObject();
}, []);
return (
<div className="mainDiv">
<div className="heading">
<h1>
<FaRegSun /> Weather
</h1>
</div>
{weatherData2.current.temp}
</div>
);
}
export default WeatherApp1;
https://i.stack.imgur.com/oqr7i.jpg
The problem with your code is that you're trying to render {weatherData2.current.temp} before the data is returned from the weather API and that's why your weatherData2 will be undefined while rendering.
You can add a loading state for checking if the data is rendering or already rendered.
You can try below code:
import React, { useState, useEffect } from "react";
import axios from "axios";
import { FaRegSun } from "react-icons/fa";
import "./stylesheets/stylesheets.css";
function WeatherApp1() {
const [loading, setLoading] = useState(true) // Loading state
const [weatherData2, setWeatherData2] = useState({});
const API_endpoint2 = `https://api.openweathermap.org/data/2.5/onecall?`;
const API_key = `2a63c27d8ba0b0d14c9e5d59f39ee1ba`;
useEffect(() => {
async function getSecondObject() {
const response = await axios.get(
`${API_endpoint2}lat=28.4360704&lon=77.021184&units=metric&appid=${API_key}`
);
setWeatherData2(response.data);
setLoading(false) // Setting the loading state to false after data is set.
}
getSecondObject();
}, []);
return (
<div className="mainDiv">
<div className="heading">
<h1>
<FaRegSun /> Weather
</h1>
</div>
{/* Checking for loading state before rendering the data */}
{loading ? (
<p>Loading...</p>
) : (
weatherData2.current.temp
)}
</div>
);
}
export default WeatherApp1;
I'm trying to memorize some values in a react component because it's re rendering even when data hasn't changed (and wont change). Using useEffect + useState the data displays correctly, but the functions are triggered each time the component is re rendered. Currently am trying to implement the useMemo hook but the async call/promise is not resolving in the render process, so it doesn't even loads the data. I'll try to give the most information out of this:
This is my AppRouter component, i create the contract and pass it as value to a provider that will be used in some other components:
import { useWeb3React } from "#web3-react/core";
import React, { useEffect, useState } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { AdminScreen } from "../components/admin/AdminScreen";
import { ContractsContext } from "../components/ContractsContext";
import { Navbar } from "../components/ui/Navbar";
import { getLibrary } from "../helpers/web3Getters";
import { useCreateContract, useGetLibrary } from "../hooks.js/contractsHooks";
import { createContract } from "../web3-utils/contractCreation";
import { MarketRoutes } from "./MarketRoutes";
import { PrivateRoute } from "./PrivateRoute";
export const AppRouter = () => {
const context = useWeb3React();
//console.log("[1] context in app router: ", context);
const { contract: kolorTokenContract, loading: loadingContract } =
useCreateContract(
context,
"0x9414f981a5B5ef2bE455f2427E2166c35e8989fB",
"abis/KolorToken.json"
);
return (
//<p>{loadingLibrary ? "library ready" : "loading library"}</p>
<ContractsContext.Provider value={[kolorTokenContract]}>
<BrowserRouter>
<Navbar />
{/* Set private route for Admining nfts & tokens */}
<Routes>
<Route
path="/admin"
element={
<PrivateRoute>
<AdminScreen />
</PrivateRoute>
}
/>
<Route path="/*" element={<MarketRoutes />} />
</Routes>
</BrowserRouter>
</ContractsContext.Provider>
);
};
The contract is then obtained from my custom context in the admin route (which is what im testing now) and then passed to one of its children:
import React, { memo, useContext, useMemo } from "react";
import { getERC20Info } from "../../helpers/tokenGetters";
import { useGetERC20Info } from "../../hooks.js/contractsHooks";
import { ContractsContext } from "../ContractsContext";
export const TokenInfo = memo((tokenContract) => {
//const { _address: ERC20Address } = tokenContract;
const { address, owner, vault, supply } = useGetERC20Info(tokenContract);
//const result = useMemo(() => getERC20Info(tokenContract), [tokenContract]);
//console.log("contract from tokeninfo:", tokenContract);
//console.log("result: ", result);
return (
<div className="row align-items-center">
<div className="col-8 col-md-6 col-sm-4 ">Minting Form</div>
<div className="col-4 col-md-3 col-sm-2 animate__animated animate__fadeInRightBig">
<h2>Kolor Token Info</h2>
<p>
Address: <b>{address}</b>
</p>
<p>
Owner: <b>{owner}</b>
</p>
<p>
Vault: <b>{vault}</b>
</p>
<p>
Current supply: <b>{supply}</b>
</p>
</div>
<hr />
</div>
);
});
Actually i'm using a custom hook with useState and useEffect to fetch the data, but it re renders the TokenInfo component even when the tokenContract hasn't changed at all. This is my custom hook:
export const useGetERC20Info = (contract) => {
//console.log("contract from usegeterc20info effect: ", contract);
const [state, setState] = useState({
address: "loading...",
owner: "loading...",
vault: "loading...",
supply: "loading",
});
useEffect(() => {
getERC20Info(contract).then(({ address, owner, vault, supply }) => {
setState({
address,
owner,
vault,
supply,
});
return () => {
setState({});
};
});
}, [contract]);
return state;
};
My getERC20Info function, tries to fetch data from the blockchain, nothing wrong with that, its working fine:
export const getERC20Info = async (contract) => {
console.log("getting erc20 info...");
console.log("contract from geterc20info: ", contract);
const { _address: address } = contract;
const owner = await getERC20Owner(contract);
const vault = await getERC20Vault(contract);
const supply = await getERC20Supply(contract);
//console.log("supply: ", supply);
return {
address,
owner,
vault,
supply,
};
};
Thanks in advance for any help!
I'm using nextjs to create a dashboard and I have the authentication using next-auth.
However, I'm trying to render the individual users data when they login to the dashboard but not sure where I'm going wrong, I know I have to use the findOne callback but for some reason I can't grab the ID or email.
Here is what I have so far
import { connectToDatabase } from '../../lib/mongodb';
import Head from 'next/head';
import Sidebar from '../components/Sidebar';
export default function Dashboard({ user }) {
return (
<>
<Head>
<title>Ellis Development - Dashboard</title>
</Head>
<Sidebar />
<section className="content dashboard-content">
<h1>Dashboard</h1>
{ users.map(user => (
<div key={user.id}>
<h2>{user.firstname} {user.lastname}</h2>
<p>{user.email}</p>
</div>
)) }
</section>
</>
)
}
// get data from database using server side rendering and mongodb connection
export async function getServerSideProps() {
const client = await connectToDatabase();
const users = await client.db().collection('users').findOne({ _id: id }).toArray();
return {
props: {
users: JSON.parse(JSON.stringify(users))
}
}
}
You can use getSession to handle server-side authentications.
check reference for more resources link
import { getSession } from "next-auth/react"
...
export async function getServerSideProps(ctx) {
const session = await getSession(ctx) //pass context to authenticate create session
const id = session.user.id //get id from session
const client = await connectToDatabase();
const user = await client.db().collection('users').findOne({ _id: id }) // No need to use toArray its returns only 1 object
return {
props: {
user
}
}
}