Next.js13异步组件中使用useState实现产品图片预览的问题求助
Next.js 13 异步组件中实现产品图片预览的解决方案
问题根源
Next.js 13 App Router 中,带 async/await 的服务器组件无法使用 React Hooks;即使加了 "use client" 标记,直接在异步组件内混用 await 和 Hooks 也不符合最佳实践,容易引发渲染逻辑冲突。
方案1:拆分组件(推荐)
将数据获取和交互逻辑分离:用异步服务器组件负责获取 Sanity 数据,把图片预览的交互部分抽成独立的客户端组件(可正常使用 useState)。
1. 改造主组件(负责数据获取)
import { getProduct } from '@/sanity/sanity-utils'; import ProductImageGallery from './ProductImageGallery'; export default async function ProductDetails({ params }) { const slug = params.product; const product = await getProduct(slug); return ( <div> <div className='product-detail-container'> <ProductImageGallery images={product.image} /> {/* 其他产品详情内容(如价格、描述等) */} </div> </div> ); }
2. 创建客户端交互组件
新建 ProductImageGallery.jsx 文件:
"use client"; import React, { useState } from 'react'; export default function ProductImageGallery({ images }) { // 初始化默认选中第一张图 const [activeIndex, setActiveIndex] = useState(0); return ( <div> <div className='image-container'> <img src={images[activeIndex]} alt="product-image" className='product-detail-image' /> </div> <div className='small-images-container'> {images.map((item, i) => ( <img src={item} alt={`product-image-${i}`} key={i} onMouseEnter={() => setActiveIndex(i)} // 可选:给当前选中的小图加高亮样式 className={i === activeIndex ? 'small-image active' : 'small-image'} /> ))} </div> </div> ); }
方案2:客户端组件内用 use Hook 处理异步数据
如果不想拆分组件,可以在客户端组件中使用 Next.js 13 提供的 use Hook 处理异步数据,避免直接在组件顶层用 await。
"use client"; import { getProduct } from '@/sanity/sanity-utils'; import React, { useState, use } from 'react'; import { AiOutlineMinus,AiOutlinePlus,AiFillStar,AiOutlineStar} from 'react-icons/ai'; export default function ProductDetails({ params }) { const slug = params.product; // 用 use Hook 处理异步数据请求 const product = use(getProduct(slug)); const [activeIndex, setActiveIndex] = useState(0); return ( <div> <div className='product-detail-container'> <div> <div className='image-container'> <img src={product.image[activeIndex]} 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} onMouseEnter={() => setActiveIndex(i)} /> ))} </div> </div> {/* 其他产品详情内容 */} </div> </div> ); }
关于 Sanity 数据获取的说明
无需调整现有 Sanity 数据获取逻辑,getProduct 函数的实现是正常的,问题仅出在 Next.js 13 的组件渲染规则上,上述两种方案均可完美解决图片预览的交互需求。
内容的提问来源于stack exchange,提问作者Martin Sosa
相关产品推荐
相关产品推荐

