Search code examples
reactjsnext.jssanitynext.js13

next.js13 fetching data with an async function and also use state?


i hope you are having a great day ! i got a question... i'm currently learning next.js 13 and so i thought doing a personal project with it would be a good idea . Currently i'm doing an e-commmerce with Sanity.io as a cms .The problem i got is that im fetching data with an async function , but i want to also use "use State" hook from react to do a nice product images viewer ! the problem i got is that i can't use a hook inside of an async function ! so please if anyone got a solution to this i would be very pleased since im really new to next.js 13 here is my code and what i want to do

"use client"
import { Product } from '@/component';
import { getProduct, getProducts } from '@/sanity/sanity-utils';
import React, { useState } from 'react';
import { AiOutlineMinus,AiOutlinePlus,AiFillStar,AiOutlineStar} from 'react-icons/ai';

export default async function ProductDetails({ params }) {
  const slug = params.product;
  const product = await getProduct(slug);
  console.log("product",product)


  return (
    <div>
      <div className='product-detail-container'>
        <div>
        <div className='image-container'>
            <img src={product.image[0]} alt="product-image"
            className='product-detail-image'
            />
          </div>
            <div className='small-images-container'>
              {product.image.map((item, i) => (
               <img
                  src={item}
                  alt={`product-image-${i}`}
                  key={i}
                  />
                 ))}
            </div>
        </div>

what i really want to do is an use State to acces the images array so it shows each image when i click on it

 const [index, setIndex] = useState(); 
 <div className='small-images-container'>
              {product.image.map((item, i) => (
               <img
                  src={item}
                  alt={`product-image-${i}`}
                  key={i}
                  onMouseEnter={()=> setIndex(i)}
                  />
                 ))}
            </div>

thank you so much i hope my questions is understandable

i actually dont know what to do honestly , should i bring the data from sanity differently ?


Solution

  • In the next 13 app router, you need to choose whether a component is a server component (only runs on server) or a client component (runs in browser). Components using hooks such as useState must be client components. On the other hand, only server components can be async. At the moment you're trying to use both at the same time, so its invalid.

    So one option is to turn your existing ProductDetails component into a bona fide client component by removing the async keyword and moving the fetching into useEffect to avoid fetching on every render (or better yet, use a fetching library such as react-query or swr).

    Alternatively, you could make ProductDetailsFetcher an async server component which fetches the data, then passes the product as a prop to a client component which does the rendering. Only the client component would have useState.

    The best option may depend on how getProduct is intended to be used - i.e. whether it designed to be run on the client or server.