Next.js 14 Server Action在RSC中使用触发ESLint错误求助
Server Component中使用Server Action的ESLint错误解决
问题描述
在Next.js 14的Server Component中编写带Server Action的表单时,触发两个ESLint错误,代码示例如下:
// /app/search/page.tsx export default function Search() { async function updateResults(formData: FormData) { "use server"; await new Promise((resolve) => setTimeout(resolve, 150)); console.log("input value => ", formData.get("me")); revalidatePath("/search"); } return ( <> <form action={updateResults}> {/* eslint errors here */} <input name="me" type="text" /> <button type="submit">Update</button> </form> </> ) }
在<form action={updateResults}>处触发的错误:
JSX props should not use functions eslint(react/jsx-no-bind)Promise-returning function provided to attribute where a void return was expected. eslint(@typescript-eslint/no-misused-promises)
当前环境:Next.js 14、ESLint 8.36,相关配置如下:
package.json依赖片段
"dependencies": { "@next/third-parties": "^14.0.4", "i18next": "^23.0.0", "js-cookie": "^3.0.5", "lodash": "^4.17.21", "next": "^14.0.3", "next-i18next": "^15.0.0", "next-intl": "^3.2.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^14.0.0", "server-only": "^0.0.1", "sharp": "^0.33.0" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.0.2", "@storybook/addon-designs": "^7.0.1", "@storybook/addon-essentials": "^7.1.0", "@storybook/nextjs": "^7.1.0", "@storybook/react": "^7.1.0", "@testing-library/dom": "^9.0.1", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", "@types/jest-axe": "^3.5.5", "@types/js-cookie": "^3.0.6", "@types/node": "^20.8.2", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@typescript-eslint/eslint-plugin": "^5.55.0", "@typescript-eslint/parser": "^5.55.0", "eslint": "^8.36.0", "eslint-config-mycustomconfig": "^12.1.0", "eslint-config-next": "^13.2.4", "eslint-config-prettier": "^8.7.0", "eslint-plugin-jest": "^27.2.1", "eslint-plugin-jest-dom": "^5.0.1", "eslint-plugin-storybook": "^0.6.12", "eslint-plugin-testing-library": "^5.11.0", "i18next-browser-languagedetector": "^7.0.2", "i18next-http-backend": "^2.2.1", "jest": "^29.5.0", "jest-axe": "^7.0.0", "jest-cli": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "postcss": "^8.4.31", "postcss-loader": "^7.1.0", "postcss-preset-env": "^8.0.1", "prettier": "^3.2.5", "sass": "^1.59.3", "sass-loader": "^13.2.0", "storybook": "^7.1.0", "storybook-react-i18next": "^2.0.6", "style-loader": "^3.3.2", "typescript": "^5.0.0" }
eslintrc.js配置
module.exports = { root: true, extends: [ "plugin:storybook/recommended", // Disable ESLint code formatting rules which conflict with Prettier "prettier", // `next` should be extended last according to their docs // https://nextjs.org/docs/basic-features/eslint "next/core-web-vitals", ], rules: { // Next.js <Image> component is useful for optimizing images, but also requires additional // dependencies to work in standalone mode. It may be overkill for most projects at // Which aren't image heavy. "@next/next/no-img-element": "off", }, // Additional lint rules. These get layered onto the top-level rules. overrides: [ // Lint config specific to Test files { files: ["tests/**"], plugins: ["jest"], extends: [ "plugin:jest/recommended", "plugin:jest-dom/recommended", "plugin:testing-library/react", ], }, // Lint config specific to TypeScript files { files: "**/*.+(ts|tsx)", parserOptions: { // These paths need defined to support rules that require type information tsconfigRootDir: __dirname, project: ["./tsconfig.json"], }, extends: [ "plugin:@typescript-eslint/recommended", // Disable vanilla ESLint rules that conflict with those in @typescript-eslint "plugin:@typescript-eslint/eslint-recommended", // Rules that specifically require type information "plugin:@typescript-eslint/recommended-requiring-type-checking", ], plugins: ["@typescript-eslint"], rules: { // Prevent dead code accumulation "@typescript-eslint/no-unused-vars": "error", // The usage of `any` defeats the purpose of typescript. Consider using `unknown` type instead instead. "@typescript-eslint/no-explicit-any": "error", }, }, ], settings: { // Support projects where Next.js isn't installed in the root directory (such as a monorepo) next: { rootDir: __dirname, }, }, };
原因分析
这两个错误都是ESLint规则未适配Next.js 14的Server Action特性导致的:
- react/jsx-no-bind:这条规则原本是防止客户端React组件内联函数引发重渲染,但Next.js在Server Component中处理表单
action时,会将内联Server Action编译为特殊的服务器端逻辑,不会触发客户端重渲染,ESLint未识别该场景。 - @typescript-eslint/no-misused-promises:规则默认认为原生表单
action应接受返回void的函数,但Server Action是异步函数(返回Promise),Next.js内部已兼容该用法,ESLint规则未更新适配。
另外,你使用的eslint-config-next版本为13.2.4,与Next.js 14主版本不匹配,旧版本配置未包含Server Action的识别逻辑,这是核心问题。
解决方法
1. 升级ESLint配置适配Next.js 14
最彻底的解决方式是将eslint-config-next升级到与Next.js主版本一致的14.x.x:
npm install eslint-config-next@^14.0.3 --save-dev # 或使用yarn yarn add eslint-config-next@^14.0.3 --dev
升级后,Next.js的ESLint规则会自动识别Server Action用法,两个错误会自动消失。
2. 临时调整ESLint规则(不升级配置的情况)
若暂时不想升级,可在TypeScript文件的规则覆盖中添加例外:
修改eslintrc.js中TypeScript overrides的rules部分:
{ files: "**/*.+(ts|tsx)", // ...其他配置 rules: { // ...原有规则 "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-explicit-any": "error", // 忽略原生DOM组件的内联函数,解决react/jsx-no-bind错误 "react/jsx-no-bind": ["error", { ignoreDOMComponents: true }], // 允许属性接受返回Promise的函数,解决no-misused-promises错误 "@typescript-eslint/no-misused-promises": [ "error", { checksVoidReturn: { attributes: false, }, }, ], }, }
3. Server Action写法优化(可选)
内联写法本身没问题,但也可以将Server Action提取到组件外部,让代码结构更清晰:
// /app/search/page.tsx async function updateResults(formData: FormData) { "use server"; await new Promise((resolve) => setTimeout(resolve, 150)); console.log("input value => ", formData.get("me")); revalidatePath("/search"); } export default function Search() { return ( <> <form action={updateResults}> <input name="me" type="text" /> <button type="submit">Update</button> </form> </> ) }
关键结论
完全可以在Server Component中直接使用Server Action,不需要把表单放到Client Component里,这是Next.js 14官方支持的特性,错误仅由ESLint配置未适配导致。
内容的提问来源于stack exchange,提问作者user19448827
相关产品推荐
相关产品推荐

