Next.js中使用React Server Components触发ReactServerComponentsError问题求助
问题描述
我在Next.js项目中使用React Server Components时遇到了编译错误,具体报错信息如下:
Failed to compile
./src\app\components\projects\slider.js
ReactServerComponentsError:
You're importing a component that needs useEffect. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.,-[C:\Users\elias\Documents\Trabalhos\Repositorios\portifolio\src\app\components\projects\slider.js:1:1]
1 | /* @jsxImportSource @react */
2 | import Image from "next/image";
3 | import "./slider.scss";
4 | import React, { useState, useEffect } from "react";
: ^^^^^^^^^
5 | import dynamic from "next/dynamic";
6 | import "owl.carousel/dist/assets/owl.carousel.css";
7 | import "owl.carousel/dist/assets/owl.theme.default.css";
`----Maybe one of these should be marked as a client entry with "use client":
./src\app\components\projects\slider.js
./src\app\page.tsx
我已经尝试给父组件page.tsx加上/* @jsxImportSource @react */,但还是报错,怀疑是不是项目没配置Babel导致的?
代码概要
slider.js 文件
/* @jsxImportSource @react */ import Image from "next/image"; import "./slider.scss"; import React, { useState, useEffect } from "react"; import dynamic from "next/dynamic"; import "owl.carousel/dist/assets/owl.carousel.css"; import "owl.carousel/dist/assets/owl.theme.default.css"; let productsp = [ { id: 1, name: "Product Number 1", brand: "Brand Name", url: "products-number-1", price: 100, }, // 省略重复的产品数据 ]; var $ = require("jquery"); if (typeof window !== "undefined") { window.$ = window.jQuery = require("jquery"); } const OwlCarousel = dynamic(() => import("react-owl-carousel"), { ssr: false, }); const Slider = () => { const options = { margin: 30, responsiveClass: true, nav: true, dots: true, autoplay: false, smartSpeed: 1000, navClass: ["owl-prev", "owl-next"], navText: ['', ''], responsive: { 0: { items: 1 }, 400: { items: 1 }, 600: { items: 2 }, 700: { items: 3 }, 1000: { items: 4 }, }, }; return ( <div className="row no-gutters"> <div className="col-xl-12 col-lg-12 col-md-12 col-sm-12 col-12 mt-3 pb-2 bg-white" id="owl-carousel-products" > <h3 className="text-center"> <span className="heading float-left w-100">Featured Products</span> </h3> <ul id="owl-carousel-ul" className="owl-carousel owl-loaded owl-drag"> <OwlCarousel className="owl-theme" loop margin={4} nav={true} navText={[ '<img src="/images/Arrow_left.png" />', '<img src="/images/Arrow_right.png" />', ]} dots={false} animateIn={true} {...options} > {productsp && productsp.length > 0 ? productsp.map((product) => ( <div id="featuredProducts" className="item float-left w-100" key={product.name} > <div className="productListing col-lg-5th col-md-4 col-sm-6 col-xs-12"> <a href={product.url}> <a className="product float-left"> <span className="image text-center"> <Image id={"img" + product.id} src="images/product.jpg" alt={product.name} title={product.name} /> </span> <span className="w-100 text-center mt-1 "> <h5 className="brand text-capitalize float-left"> {product.brand} </h5> <span className="name">{product.name}</span> <strong className="itemPrice p-0"> Price: {product.price} </strong> </span> </a> </a> </div> </div> )) : ""} </OwlCarousel> </ul> </div> </div> ); }; export default Slider;
page.tsx 文件
import { Experience } from './components/experience/experience' import { Header } from './components/header/header' import { Info } from './components/information/information' import Slider from './components/projects/slider' import "./styles/home.scss" import { EmailIcon } from './components/icons/email-icon' import { SocialBtns } from './components/social-btns/social-btns' export default function Home() { return ( <main className="container"> <Header /> <Experience /> <Info /> <div className="buttons"> <SocialBtns /> <a className="btn-primary" href="mailto:elias.souza.es609@gmail.com"> contact me <EmailIcon /> </a> </div> <Slider /> </main> ) }
解决方案
别担心,这个问题和Babel完全没关系!Next.js已经内置了处理React Server Components的所有工具,不需要额外配置Babel。
错误的核心原因是:Next.js 13+的App Router默认所有组件都是Server Components,而useState、useEffect这类React客户端钩子,以及依赖浏览器环境的库(比如jQuery、owl-carousel)只能在Client Components里运行。你之前加的/* @jsxImportSource @react */是用来指定JSX编译源的,和标记Client Component完全不相关,所以没用。
正确的做法很简单:
- 在**
slider.js文件的最顶部**添加"use client";指令,这个指令必须放在所有import语句之前,告诉Next.js这个组件是Client Component:
"use client"; /* @jsxImportSource @react */ import Image from "next/image"; import "./slider.scss"; import React, { useState, useEffect } from "react"; // 后续代码保持不变
不需要给page.tsx加这个指令(除非你想把整个页面都变成Client Component),我们只需要把实际用到客户端逻辑的Slider组件标记为Client Component就好,这样其他组件还能保留Server Component的优势(比如服务器端渲染、减少客户端JS体积)。
另外,你的Slider组件里还用到了jQuery和react-owl-carousel,这些都依赖浏览器的window对象,只有在Client Component里才能正常运行,所以标记"use client"是必须的。
备注:内容来源于stack exchange,提问作者Elias Souza

