Skip to content

eslint 插件

eslint-plugin-react-hooks-ref

  • 强制要求 useRef 的变量必须用const声明且以Ref结尾,并且能够自动修复

插件实现

js
// plugins/eslint-plugin-react-hooks-ref.js
export default {
  rules: {
    // 规则名称
    "enforce-ref-naming": {
      meta: {
        type: "problem",
        docs: {
          description:
            "强制要求 useRef 的变量必须用 const 声明且以 Ref 结尾,并确保正确使用 .current",
          recommended: true,
        },
        fixable: "code",
        schema: [],
        hasSuggestions: true,
      },
      create(context) {
        // 存储所有 useRef 变量名
        const refVariables = new Set();

        return {
          // 检查变量声明
          VariableDeclarator(node) {
            const isUseRefCall =
              node.init &&
              node.init.type === "CallExpression" &&
              node.init.callee.name === "useRef";

            if (isUseRefCall && node.id.type === "Identifier") {
              const varName = node.id.name;
              refVariables.add(varName);

              // 检查 const 声明
              if (node.parent.kind !== "const") {
                context.report({
                  node: node.parent,
                  message: "useRef 的变量必须用 const 声明",
                  fix(fixer) {
                    return fixer.replaceTextRange(
                      [
                        node.parent.start,
                        node.parent.start + node.parent.kind.length,
                      ],
                      "const"
                    );
                  },
                });
              }

              // 检查变量名后缀
              if (!varName.endsWith("Ref")) {
                context.report({
                  node: node.id,
                  message: "useRef 的变量名必须以 Ref 结尾",
                  fix(fixer) {
                    return fixer.insertTextAfter(node.id, "Ref");
                  },
                });
              }
            }
          },

          // 检查 MemberExpression (如 a.current)
          MemberExpression(node) {
            if (
              node.property.type === "Identifier" &&
              node.property.name === "current" &&
              node.object.type === "Identifier"
            ) {
              const objectName = node.object.name;
              if (!refVariables.has(objectName) && objectName.endsWith("Ref")) {
                context.report({
                  node,
                  message: `使用 ${objectName} 时应该用 ${objectName.replace(
                    "Ref",
                    ""
                  )}.current`,
                  fix(fixer) {
                    return fixer.replaceText(
                      node.object,
                      objectName.replace("Ref", "")
                    );
                  },
                });
              } else if (
                !objectName.endsWith("Ref") &&
                refVariables.has(objectName + "Ref")
              ) {
                context.report({
                  node,
                  message: `使用 ${objectName}.current 时应该用 ${objectName}Ref.current`,
                  fix(fixer) {
                    return fixer.replaceText(node.object, `${objectName}Ref`);
                  },
                });
              }
            }
          },
        };
      },
    },
  },
};

引用插件

js
// eslint.config.js
import reactHooksRef from "./plugins/eslint-plugin-react-hooks-ref.js";
export default [
//   ...其他配置
    plugins: {
      "react-hooks-ref": reactHooksRef, // 加载自定义插件
    },
    rules: {
      "react-hooks-ref/enforce-ref-naming": "error", // 启用规则,违反时报错

    },
];