I am upgrading my ESLint configuration from 8 to 9.14.0. But now after creating the eslint.config.js
file, when I am running npx eslint
, it is giving me weird errors. For example I have a component file named QuestionnaireTabNavigator.js
, which is using some components and I am using them in my code but still getting issue in ESLint like:
3:10 error 'Tab' is defined but never used no-unused-vars
3:15 error 'Tabs' is defined but never used no-unused-vars
9:8 error 'AssessmentSelector' is defined but never used no-unused-vars
10:8 error 'AssessmentsTable' is defined but never used no-unused-vars
11:8 error 'ErrorBoundaryWrapper' is defined but never used no-unused-vars
12:8 error 'Loader' is defined but never used no-unused-vars
13:8 error 'QuestionnaireDetailedResults' is defined but never used no-unused-vars
14:8 error 'QuestionnaireSummary' is defined but never used
What is the issue?
eslint.config.mjs
:
import babelParser from "@babel/eslint-parser";
import pluginJs from "@eslint/js";
import importConfig from "eslint-plugin-import";
import pluginJest from "eslint-plugin-jest";
import jsdocConfig from "eslint-plugin-jsdoc";
import jsoncConfig from "eslint-plugin-jsonc";
import pluginPrettier from "eslint-plugin-prettier";
import pluginReact from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import sortKeysFixConfig from "eslint-plugin-sort-keys-fix";
import spellcheck from "eslint-plugin-spellcheck";
import testingPlugin from "eslint-plugin-testing-library";
import globals from "globals";
const config = [
pluginJs.configs.recommended,
{
"files": ["**/*.{js,mjs,cjs,jsx}"],
"languageOptions": {
"ecmaVersion": 2020,
"globals": {
...globals.es2021,
...globals.node,
...globals.browser,
"describe": "readonly",
"expect": "readonly",
"indexedDB": "readonly",
"it": "readonly",
"jest": "readonly",
"test": "readonly"
},
"parser": babelParser,
"parserOptions": {
"babelOptions": {
"babelrc": false,
"configFile": false,
"presets": [["@babel/preset-react", { "runtime": "automatic" }], "@babel/preset-env"]
},
"ecmaFeatures": {
"jsx": true
},
"requireConfigFile": false,
"sourceType": "module"
}
},
"plugins": {
"import": importConfig,
"jsdoc": jsdocConfig,
"jsonc": jsoncConfig,
"prettier": pluginPrettier,
"react": pluginReact,
"react-hooks": reactHooks,
"sort-keys-fix": sortKeysFixConfig,
"spellcheck": spellcheck,
"testing-library": testingPlugin
},
"rules": {
"array-bracket-newline": ["error", "consistent"],
"array-bracket-spacing": ["error", "never"],
"array-callback-return": "error",
"array-element-newline": [
"error",
{
"ArrayExpression": "consistent",
"ArrayPattern": { "minItems": 3 }
}
],
"arrow-spacing": "error",
"brace-style": ["error", "1tbs"],
"camelcase": ["error", { "ignoreDestructuring": true, "properties": "never" }],
"comma-dangle": ["error", "never"],
"comma-spacing": [
"error",
{
"after": true,
"before": false
}
],
"curly": ["error", "all"],
"default-case": "off",
"eqeqeq": ["error", "always"],
"func-call-spacing": ["error", "never"],
"function-call-argument-newline": ["error", "consistent"],
"import/first": "off",
"import/order": ["error", { "groups": ["builtin", "external", "internal", "parent", "sibling", "index"] }],
"indent": ["error", 4, { "SwitchCase": 1, "ignoredNodes": ["ConditionalExpression"] }],
"jest/no-mocks-import": "off",
"jsdoc/newline-after-description": 0,
// Required for vs code auto formatting
"jsdoc/require-hyphen-before-param-description": 1,
"jsdoc/require-jsdoc": [
"error",
{
"require": {
"ArrowFunctionExpression": true,
"ClassExpression": true,
"FunctionDeclaration": true,
"FunctionExpression": true,
"MethodDefinition": true
}
}
],
"jsonc/sort-keys": "error",
"keyword-spacing": ["error", { "after": true, "before": true }],
"max-len": [
"error",
{
"code": 120,
"ignorePattern": "".*": ".*"" // Ignore pattern for strings in json as they can't be broken in multi lines
}
],
"no-console": "error",
"no-dupe-else-if": "error",
"no-extend-native": "off",
"no-nested-ternary": "error",
// "no-unused-vars": "warn",
"no-useless-escape": "off",
"no-var": "error",
"object-curly-newline": ["error", { "consistent": true, "multiline": true }],
"object-curly-spacing": ["error", "always"],
"object-property-newline": ["error", { "allowAllPropertiesOnSameLine": true }],
"padding-line-between-statements": [
"error",
// Always one empty line before return statement
{
"blankLine": "always",
"next": "return",
"prev": "*"
},
// Always one empty line between methods
{
"blankLine": "always",
"next": ["block-like", "multiline-block-like"],
"prev": ["block-like", "multiline-block-like"]
},
// Avoids more than one empty line
{
"blankLine": "never",
"next": "empty",
"prev": "empty"
}
],
"prefer-const": "error",
"quote-props": ["error", "always"],
"quotes": ["error", "double"],
"radix": "off",
"react-hooks/exhaustive-deps": "error",
"react-hooks/rules-of-hooks": "error",
"react/jsx-boolean-value": ["warn", "always"],
"react/jsx-closing-bracket-location": "error",
"react/jsx-closing-tag-location": "error",
"react/jsx-curly-brace-presence": [
"error",
{
"children": "ignore",
"props": "always"
}
],
"react/jsx-tag-spacing": ["error", { "beforeSelfClosing": "always" }],
"react/no-multi-comp": ["error", { "ignoreStateless": true }],
"react/react-in-jsx-scope": "off",
"semi": ["error", "always"],
"sort-keys": [
"error",
"asc",
{
"caseSensitive": true,
"minKeys": 2,
"natural": false
}
],
"sort-keys-fix/sort-keys-fix": "error",
"space-before-blocks": "error",
"space-infix-ops": ["error", { "int32Hint": false }],
"testing-library/no-node-access": "off"
},
"settings": {
"react": {
// Only needed for older React versions
"fragment": "Fragment",
// Automatically detect React version
"pragma": "React",
// Only needed for older React versions
"runtime": "automatic",
"version": "detect" // Use React 17+ JSX transform
}
}
},
{
"files": ["**/*.test.{js,mjs,cjs,jsx}"],
"languageOptions": {
"globals": {
...globals.jest,
...globals.browser,
...globals.node,
"describe": "readonly",
"expect": "readonly",
"indexedDB": "readonly",
"it": "readonly",
"jest": "readonly",
"test": "readonly"
}
},
"plugins": {
"jest": pluginJest
},
"rules": {
"react/react-in-jsx-scope": "off"
},
"settings": {
"react": {
"version": "detect"
}
}
}
];
export default config;
package.json
file:
{
"browserslist": [
">0.2%",
"not dead",
"not op_mini all"
],
"dependencies": {
"@types/jest": "^29.5.11",
"ajv": "^8.17.1",
"awesome-debounce-promise": "~2.1.0",
"axios": "~1.7.2",
"bootstrap": "~5.3.2",
"dotenv": "^16.4.1",
"i18next": "~23.15.1",
"i18next-http-backend": "~2.6.1",
"jest-mock-axios": "~4.7.3",
"lodash": "~4.17.21",
"moment": "~2.30.1",
"moment-timezone": "^0.5.43",
"react": "~18.3.1",
"react-bootstrap": "~2.10.2",
"react-bootstrap-icons": "~1.11.4",
"react-component-export-image": "~1.0.6",
"react-dom": "~18.3.1",
"react-draggable": "^4.4.6",
"react-dropzone": "^14.2.3",
"react-error-boundary": "^4.0.13",
"react-i18next": "~15.0.1",
"react-icons": "~5.3.0",
"react-infinite-scroller": "~1.2.6",
"react-intersection-observer": "~9.13.0",
"react-leaflet": "~4.2.1",
"react-multi-select-component": "~4.3.4",
"react-router": "~6.26.2",
"react-router-dom": "~6.26.2",
"react-router-prompt": "^0.7.0",
"react-scroll": "~1.9.0",
"react-select": "~5.8.0",
"react-switch": "~7.0.0",
"react-tabs": "~6.0.2",
"react-toastify": "~10.0.4",
"socket.io-client": "~4.8.0",
"use-csv-downloader": "0.0.0",
"web-vitals": "~4.2.2"
},
"devDependencies": {
"@babel/eslint-parser": "~7.25.9",
"@eslint/js": "^9.14.0",
"@testing-library/dom": "~10.4.0",
"@testing-library/jest-dom": "~6.6.3",
"@testing-library/react": "~16.0.1",
"@testing-library/user-event": "~14.5.1",
"@typescript-eslint/eslint-plugin": "~8.13.0",
"@typescript-eslint/parser": "~8.13.0",
"eslint": "~9.14.0",
"eslint-config-prettier": "~9.1.0",
"eslint-config-react-app": "~7.0.1",
"eslint-plugin-import": "~2.31.0",
"eslint-plugin-jest": "~28.9.0",
"eslint-plugin-jsdoc": "^50.5.0",
"eslint-plugin-jsonc": "~2.18.1",
"eslint-plugin-jsx-a11y": "~6.10.2",
"eslint-plugin-prettier": "~5.2.1",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "~5.0.0",
"eslint-plugin-sort-keys-fix": "~1.1.2",
"eslint-plugin-spellcheck": "0.0.20",
"eslint-plugin-testing-library": "~6.4.0",
"fake-indexeddb": "6.0.0",
"globals": "^15.12.0",
"pre-commit": "~1.2.2",
"prettier": "~3.3.3",
"prettier-eslint": "~16.3.0",
"prettier-eslint-cli": "~8.0.1",
"react-scripts": "~5.0.1",
"sass": "~1.80.6",
"stylelint": "~16.10.0",
"stylelint-config-prettier": "~9.0.5",
"stylelint-config-standard-scss": "~13.1.0",
"stylelint-prettier": "~5.0.2",
"stylelint-scss": "~6.8.1"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"jest": {
"collectCoverageFrom": [
"src/**/*.js",
"!src/**/*.scss",
"!src/assets/**/*.js"
],
"coverageThreshold": {
"global": {
"branches": 50,
"functions": 50,
"lines": 65,
"statements": 65
}
}
},
"name": "imp-fe",
"pre-commit": [
"lint-pre-commit",
"test-pre-commit"
],
"private": true,
"scripts": {
"build": "react-scripts build",
"eject": "react-scripts eject",
"format": "npm run prettier-fix && npm run lint-fix",
"lint": "npm run lint-check && npm run stylelint",
"lint-check": "eslint src/ --ignore-pattern 'src/__mocks__/'",
"lint-fix": "npm run lint-check -- --fix",
"lint-pre-commit": "FILE_DIFF=$(git diff --name-only --cached --diff-filter=ACMR | grep .js$ | sed 's#/[^/]*$##') && [ '$FILE_DIFF' != '' ] && (eslint --ext .jsx --ext .js --ext .json $(echo $FILE_DIFF) && npm run stylelint) || exit 0",
"prettier-fix": "npx prettier --write src/",
"pull-latest-and-verify": "npm run pull-latest-code && node ./.dev/check_if_env_latest.js",
"pull-latest-code": "git pull && npm i",
"start": "react-scripts start",
"stylelint": "npx stylelint 'src/**/*.{css,scss}'",
"stylelint-fix": "npm run stylelint -- --fix",
"test": "react-scripts test --maxWorkers 1",
"test-coverage": "npm run test -- --coverage --watchAll=false",
"test-no-watch": "npm run test -- --watchAll=false",
"test-pre-commit": "FILE_DIFF=$(git diff --name-only --cached --diff-filter=ACMR | grep .js$ | sed 's#/[^/]*$##') && [ '$FILE_DIFF' != '' ] && (npm run test-no-watch -- $(echo $FILE_DIFF) --passWithNoTests) || exit 0"
},
"version": "24.8.0"
}
Component file QuestionnaireTabNavigator.js
:
import PropTypes from "prop-types";
import { useCallback, useContext, useState } from "react";
import { Tab, Tabs } from "react-bootstrap";
import { withTranslation } from "react-i18next";
import { useNavigate, useSearchParams } from "react-router-dom";
import { CATEGORY_WISE_DATA_STRUCTURE, CATEGORY_WISE_DETAILED_RESULT } from "../../App.Constants";
import { QuestionnaireContext } from "../../contexts/QuestionnaireContext";
import { UserContext } from "../../contexts/UserContext";
import AssessmentSelector from "../AssessmentSelector/AssessmentSelector";
import AssessmentsTable from "../AssessmentsTable/AssessmentsTable";
import ErrorBoundaryWrapper from "../ErrorBoundaryWrapper/ErrorBoundaryWrapper";
import Loader from "../Loaders/Loader/Loader";
import QuestionnaireDetailedResults from "../QuestionnaireDetailedResults/QuestionnaireDetailedResults";
import QuestionnaireSummary from "../QuestionnaireSummary/QuestionnaireSummary";
import "./QuestionnaireTabNavigator.scss";
/**
*
* @param {*} props - Parent props
* @returns {Element} - Returns multiple tabs with different sub components
*/
const QuestionnaireTabNavigator = ({ t }) => {
const navigate = useNavigate();
const { fetchAssessmentsList, "allAssessmentsData": apiResponse } = useContext(QuestionnaireContext);
const { user } = useContext(UserContext);
const [searchParams] = useSearchParams();
const section = searchParams.get("section");
/**
* Added this extra check for apiResponse for smooth transitioning.
* Loader will be visible based on current state value.
* Also updating these state values from AssessmentSelector component.
*/
const [benchmarkingData, setBenchmarkingData] = useState(!apiResponse.length ? CATEGORY_WISE_DATA_STRUCTURE : null);
const [detailedResults, setDetailedResults] = useState(!apiResponse.length ? CATEGORY_WISE_DETAILED_RESULT : null);
let tab = "assessments";
if (window.location.pathname.includes("summary")) {
tab = "summary";
} else if (window.location.pathname.includes("detailed-results")) {
tab = "detailed-results";
}
const [selectedTab, setSelectedTab] = useState(tab);
const handleTabSelect = useCallback(
(tab) => {
if (tab === "detailed-results") {
navigate(
`/${user.selectedTenant}/maturity-assessment/detailed-results${
section ? `section=${section}` : ""
}`,
{
"state": { "redirect": "no" }
}
);
} else if (tab === "summary") {
navigate(`/${user.selectedTenant}/maturity-assessment/summary`, {
"state": { "redirect": "no" }
});
} else {
navigate(`/${user.selectedTenant}/maturity-assessment/all-assessments`, {
"state": { "redirect": "no" }
});
}
},
[navigate, section, user.selectedTenant]
);
const setStateMethod = selectedTab === "detailed-results" ? setDetailedResults : setBenchmarkingData;
return (
<div
className={"QuestionnaireTabNavigator bottom-border-tab-variant"}
data-testid={"QuestionnaireTabNavigator"}
>
{selectedTab !== "assessments" ? (
<AssessmentSelector
assessmentsList={apiResponse}
selectedTab={selectedTab}
setStateMethod={setStateMethod}
/>
) : null}
<Tabs
activeKey={selectedTab}
id={"questionnaire-tab-navigator"}
transition={false}
onSelect={handleTabSelect}
>
<Tab eventKey={"assessments"} title={t("questionnaire.tab_navigator.assessments")}>
<ErrorBoundaryWrapper componentTitle={"Assessment table"}>
<AssessmentsTable apiResponse={apiResponse} fetchAssessmentsList={fetchAssessmentsList} />
</ErrorBoundaryWrapper>
</Tab>
<Tab eventKey={"summary"} title={t("questionnaire.tab_navigator.summary")}>
<ErrorBoundaryWrapper componentTitle={"Questionnaire Summary"}>
{benchmarkingData ? (
<QuestionnaireSummary benchmarkingData={benchmarkingData} setSelectedTab={setSelectedTab} />
) : (
<Loader />
)}
</ErrorBoundaryWrapper>
</Tab>
<Tab eventKey={"detailed-results"} title={t("questionnaire.tab_navigator.detailed-results")}>
<ErrorBoundaryWrapper componentTitle={"Questionnaire Detailed Results"}>
{detailedResults ? (
<QuestionnaireDetailedResults detailedResults={detailedResults} />
) : (
<Loader />
)}
</ErrorBoundaryWrapper>
</Tab>
</Tabs>
</div>
);
};
QuestionnaireTabNavigator.propTypes = {
"t": PropTypes.func
};
export default withTranslation()(QuestionnaireTabNavigator);
2
Answers
I suggest you follow the migration guide from the ESLint documentation. You can start using the configuration migrator on your existing configuration file.
This issue arises because ESLint is enforcing the
no-unused-vars
rule, which flags any variables, imports, or components that are defined but not used in your code. This is often helpful to prevent unnecessary or redundant code, but it can be annoying when you are in the process of refactoring or temporarily not using a variable.Here are several ways to resolve or suppress these errors:
Change ESLint Rule Configuration
If you prefer to keep the unused variables (perhaps for future use or refactoring purposes), you can modify the ESLint configuration to either:
Warn instead of error:
This will prevent the ESLint errors but still notify you about unused variables:
In your
.eslintrc.js
oreslint.config.mjs
, change theno-unused-vars
rule to"warn"
:Turn off the rule entirely:
If you don’t want ESLint to check for unused variables at all, you can disable the rule entirely:
Conclusion