かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

React v17 create-react-app で作ったアプリで ESLint に怒られまくった

npx create-react-app で React のアプリを作ったら v17 系になっていました。
今までのプロジェクトで使っていた ESLint の設定を持ってきたらエラーになる部分があったのでメモ

  • react 17.0.1
  • react-scripts 4.0.1
  • eslint 7.14.0
  • eslint-plugin-react 7.21.5

.eslintrc.js (前使ってたのをコピペして持ってきた)

module.exports = {
  env: {
    browser: true,
    es6: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:prettier/recommended',
    'prettier',
    'prettier/react',
    'prettier/standard',
  ],
  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
  },
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  plugins: ['react', 'prettier'],
  root: true,
  rules: {
    'eol-last': ['error', 'always'],
    'indent': ['error', 2, { SwitchCase: 1 }],
    'newline-before-return': 'error',
    'no-console': 'warn',
    'no-dupe-class-members': 'error',
    'no-var': 'error',
    'object-shorthand': ['error', 'always'],
    'prefer-arrow-callback': 'error',
    'prefer-const': 'error',
    'prefer-spread': 'error',
    'react/prop-types': 'off',
    'require-yield': 'error',
  },
};

JSX で React をインポートしてないと 'React' must be in scope when using JSX エラー

React v17 からは JSX を使っているファイルに import React from 'react'; を書かなくてもOKになっていました。(使ってるように見えない React を import しなくて良くなったのは個人的には嬉しい)

従来、JSX記法を使用するファイルでは以下の記述を含める必要がありました。

import React from "react";
これは、JSXが React.createElement に変換されるのであらかじめこちら側で React を用意しておく必要があったからです。新しい記法では、React をあらかじめインポートしておく必要がありません。 cf. [https://zenn.dev/uhyo/articles/react17-new-jsx-transform:title]

React v17 からは JSX を React.createElement の形式ではなく、動的にインポートされる _jsxs を使って変換するようになったので React をインポートしなくても済むようになったみたいです。
しかし ESLint で plugin:react/recommended を指定していると JSX で import React from 'react' が無いとエラーになってしまっていました。

.eslintrc.js

module.exports = {
  //...
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    // …
  ],
  // … 
}

create-react-app で作られた App.js には import React from 'react' が無いのでエラーになる。

// App.js
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      {/* 略 */}
    </div>
  );
}

=> error 'React' must be in scope when using JSX

react/react-in-jsx-scope のルールを off にする

v17 以前は import React from 'react' が無いとエラーになっていたので、おそらく書き忘れ防止のルールだと思うのですが、react/react-in-jsx-scope が recommended で有効になっているので、これを off にしてあげれば OK

.eslintrc.js

module.exports = {
  // …
  rules: {
    // …
   'react/react-in-jsx-scope': 'off',
  },
}

cf. eslint-plugin-react/react-in-jsx-scope.md at edbbd7931902ec0b1cc1941df4155e39acc06f31 · yannickcr/eslint-plugin-react · GitHub

₍ ᐢ. ̫ .ᐢ ₎👌


テストファイルで 'test' is not defined, 'expect' is not defined のエラー

Jest が使われているテストファイルで、Jest の 関数が定義されてないというエラー

// src/App.test.js
import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/iu);
  expect(linkElement).toBeInTheDocument();
});

=> 'test' is not defined
=> 'expect' is not defined

Jest を import してるわけでもないので、エラーが出るのもまぁ解るって感じ。

Jest の関数を global にすれば OK

$ npm install --save-dev eslint-plugin-jest

.eslintrc.js

module.exports = {
  env: {
    browser: true,
    es6: true,
    node: true,
    'jest/globals': true, // ←追加
  },
  // …
  plugins: ['react', 'prettier', 'jest'], // 'jest' を追加
};

cf. eslint-plugin-jest - npm

₍ ᐢ. ̫ .ᐢ ₎👌


Dynamic import で import' and 'export' may only appear at the top level エラー

生成された reportWebVitals.js 内で Dynamic import が使われていてこれがエラーになっていました。

// reportWebVitals.js
const reportWebVitals = (onPerfEntry) => {
  if (onPerfEntry && onPerfEntry instanceof Function) {
    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
      // ...
    });
  }
};

=> error Parsing error: 'import' and 'export' may only appear at the top level

parserOptions ecmaVersion 2020 を指定

parserOptions.ecmaVersion2020 にすれば Dynamic imports がサポートされる

.eslintrc.js

module.exports = {
  // …
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
-   ecmaVersion: 2018,
+   ecmaVersion: 2020,
    sourceType: 'module',
  },
}

cf. ESLint v6.2.0 - Qiita

₍ ᐢ. ̫ .ᐢ ₎👌

babel-eslint を使う

検索すると parser に babel-eslint を使う方法が結構ヒットする。
ESLint が ES2020 をサポートする以前は babel-eslint を使うことで Dynamic imports を通していたっぽい。
parserOptionsecmaVersion: 2018 でも babel-eslint の設定をすれば Dynamic imports を使ってもエラーにならないようにできた。

$ npm install --save-dev babel-eslint

.eslintrc.js

module.exports = {
  parser: 'babel-eslint', // ← 追加
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  // …
}

eslint v6.2.0 以降では parserOptions.ecmaVersion: 2020 とすれば Dynamic imports 大丈夫なので、2020 に出来ない理由がない限りこの方法は使わなくても良さそう。(create-react-app で作ったアプリは内部的に babel-eslintを使ってる)

所管

これで creat-react-app で作ったままのアプリで ESLint が通るようになりました!
webpack もだけど設定ファイルって一回作ったらコピペで使いまわしちゃうから、変更が入ってエラーが出てしまうと毎回これ何の設定だったっけ〜ってなってしまう。(記憶力0)
設定を作る機会が少ないから記憶に定着しないんだよね…って言い訳させてくれ。


[参考]

ESLint 関連の記事に Lindt 貼りがち〜