I am new to NextJS so the question might sound a trivial so please bear with me. So, basically I want to fetch data from a database and display it in the page at the first render and for that I am trying to use useEffect and useState hooks as follows.
"use client"
import axios from "axios"
import Link from "next/link"
import { useEffect } from "react"
async function ProductsComponent( ) {
const [products,setProducts] = useState<any[]>([])
useEffect(()=>{
async function fetchProducts() {
"use server"
try {
const productsresponse = await axios.get(`${process.env.BASE_URL}/api/productControllers`)
if(productsresponse.status === 200){
setProducts(productsresponse.data.message)
}else{
console.log("Failed to fetch Products")
}
} catch (error) {
}
}
fetchProducts()
},[])
return (
<div className=" flex flex-col">
<h3>Product Name</h3>
{
products?.map(product=>(
<div>
<h4>{product?.productName}</h4>
</div>
</div>
))
}
</div>
)
}
export default ProductsComponent
but I get an error of:
async/await is not yet supported in Client Components, only Server Components.
and if i remove the "use client"
, I get an error of:
You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
So how can I ever fetch data from database and render it in NextJS >=13 ?
I tried defining a separate server side function component that fetches data from database using useEffect and the compoentnt takes the product and setproduct as props and I passed the component in anohter client component where we define states like product ,setProduct but I am still getting an error.
There are two ways to render a page:
Client Side - client gets page & it makes api call & then sets data in page.
Server side - server gets data & generates page & then sends it to the client.
Two solutions :
Client side rendering, make a component with 'use client'
at top of
it & import it in your page.
Server side rendering (make page on server & then send to client)
Read these concepts, for more clarity:
Server Components https://nextjs.org/docs/app/building-your-application/rendering/server-components
Using Server Components in Next.js https://nextjs.org/docs/app/building-your-application/rendering/server-components#using-server-components-in-nextjs
Benefits of Client Rendering https://nextjs.org/docs/app/building-your-application/rendering/client-components#benefits-of-client-rendering
'use client' https://react.dev/reference/react/use-client
When to use 'use client' https://react.dev/reference/react/use-client#when-to-use-use-client
Client Components https://nextjs.org/docs/app/building-your-application/rendering/client-components
Using Client Components in Next.js https://nextjs.org/docs/app/building-your-application/rendering/client-components#using-client-components-in-nextjs
Rendering : https://nextjs.org/docs/app/building-your-application/rendering
Server and Client Composition Patterns https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns
As you said
display it in the page at the first render
So SSR would be the way to go, as it will generate page & send it to the client. No Loading states, will be seen.
I'm using NextJS version : 13.5.4
By default Next.js considers a component as Server Component. But when you use use client
in a component you make it as client component.
Hence it throws a error when u remove 'use client'
from a component which uses (useEffect,useState etc. as this are hydrated/calculated on client side).
You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
Here is code with both implementations.
Folder Structure :
projectName
├── .gitignore
├── jsconfig.json
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│ ├── images
│ ├── next.svg
│ └── vercel.svg
├── README.md
├── src
│ └── app
│ ├── api
│ ├── comp
│ │ └── ProductsList.js
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.js
│ ├── page.js
│ ├── products
│ │ └── page.js
│ └── products_clientside
│ └── page.js
└── tailwind.config.js
loc\src\app\products\page.js
import axios from "axios";
async function GetProducts() {
let { data } = await axios.get('https://dummyjson.com/products')
return data
}
export default async function ProductsPage() {
let Products = await GetProducts();
console.log("Data from API on Serverside :", Object.keys(Products));
// console.log("Products on Serverside :", Products.products);
return (
<div>
<h1>Products Page (Serverside Fetching)</h1>
{
Products
?
Products.products.map((p, i) => {
return (
<p key={i}>{p.title}</p>
)
})
:
"Getting Products ...."
// THIS LOADING STATE WILL NOT BE VISIBLE BECAUSE SERVER LOADS THIS WHOLE PAGE
}
</div>
)
}
Explaination :
Output :
http://localhost:3000/products
-----------------------------------------------------------------------------
Client side rendering:
ProductsList Component loc\src\app\comp\ProductsList.js
'use client'
import axios from 'axios'
import React, { useEffect, useState } from 'react'
const ProductsList = () => {
async function GetProducts() {
let { data } = await axios.get('https://dummyjson.com/products')
console.log(data);
SetProducts(data)
}
const [Products, SetProducts] = useState(null)
useEffect(() => {
GetProducts()
}, [])
return (
<div>
<h1>Products Page</h1>
{
Products
?
Products.products.map((p, i) => {
return (
<p key={i}>{p.title}</p>
)
})
:
"Getting Products ...."
// THIS LOADING STATE WILL BE VISIBLE BECAUSE CLIENT LOADS A SECTION OF PAGE
}
</div>
)
}
export default ProductsList
Now make a page loc\src\app\products_clientside\page.js
& import ProductsList
import ProductsList from "../comp/ProductsList"
const page = () => {
return (
<div>
<h1> Product Page Other Way (Client Side Fetching)</h1>
<ProductsList />
</div>
)
}
export default page
Explaination :
Getting Products ....
& when it gets data sets it & shows.Output :
Go to url http://localhost:3000/products_clientside
You will see "Getting Products ...."
& also in console the data recieved from api
Go in the Network tab in present beside the console, open this page, you will see products_clientside
under Name, click on it & then on right hand side click on Preview Tab
you will see page is rendered till
Product Page Other Way (Client Side Fetching) Products Page Getting Products .... After that it Products state is set & rendered by client( client side rendering - browser does it)
If you have any doubts, then please leave a comment (I will update answer if necessary)