
closeはウィンドウを閉じるメソッド
先日、「【JavaScript】関数名をclose()にするのは気をつけろ」という記事が話題になっていました。
closeはウィンドウ(タブ)を閉じるメソッドなので、closeを上書きせずに onClick={close} を書くとボタンを押したときにウィンドウを閉じてしまいます。
closeをモーダルを閉じる関数として作成した場合、その関数を削除したままにしておくと、closeをonClickなどで実行した際にウィンドウが閉じてしまうという重大なバグが発生します。
closeは元々あるメソッドなので、const closeの部分を削除しても警告は表示されません。
試しに以下のコマンドでReact環境を作成して、App.tsxのコードを onClick={close} に変更してみてください。ボタンを押すとウィンドウ(タブ)を閉じます。
npm create vite@latest my-react-close -- --template react-swc-ts
function App() {
// const close = () => {
// モーダルを閉じる処理
// }
return <button onClick={close}>モーダルを閉じる</button>
}
export default App
no-restricted-globalsでcloseを設定する
closeを設定してもエラーにならない問題はESLintでno-restricted-globalsに ['error', 'close'] を設定することで解決できます。
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
// 中略
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'no-restricted-globals': ['error', 'close'],
},
},
)
実際はclose以外にもopenやstopなどもあるので、'no-restricted-globals' を設定する際は以下のように設定することをオススメします。
nameとmessageを付けると使ってはいけない理由もESLintの警告時に表示できます。
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
const noRestrictedDefaultMessage = 'JavaScriptの組込み関数です。'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
// 中略
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'no-restricted-globals': [
'error',
{
'name': 'alert',
'message': noRestrictedDefaultMessage + 'アラートを開いてしまいます。',
},
{
'name': 'blur',
'message': noRestrictedDefaultMessage,
},
{
'name': 'close',
'message': noRestrictedDefaultMessage + 'ウィンドウ(タブ)を閉じてしまいます。',
},
{
'name': 'confirm',
'message': noRestrictedDefaultMessage + 'メッセージのないダイアログを表示してしまいます。',
},
{
'name': 'focus',
'message': noRestrictedDefaultMessage,
},
{
'name': 'open',
'message': noRestrictedDefaultMessage + 'タブを開いてしまいます。',
},
{
'name': 'print',
'message': noRestrictedDefaultMessage + '印刷画面を開いてしまいます。',
},
{
'name': 'prompt',
'message': noRestrictedDefaultMessage + 'メッセージのないダイアログを表示してしまいます。',
},
{
'name': 'stop',
'message': noRestrictedDefaultMessage + 'ページのリソースの読み込みを停止してしまいます。',
},
],
},
},
)
なぜこれらがダメなのかについて、close以外も解説します。
open
closeとは逆にタブを開いてしまいます。
TypeScriptを使用している場合は「Type '(url?: string | URL | undefined,〜」の警告が表示されるので、closeよりは間違いにくいです。
function App() {
// 別タブを開いてしまう
return <button onClick={open}>button</button>
}
export default App
stop
ページのリソースの読み込みを停止してしまいます。
画像やAPIのデータなどの読込中にstopが実行されると、それらが停止します。
function App() {
// ページのリソースの読み込みを停止してしまう
return <button onClick={stop}>button</button>
}
export default App
印刷画面を開いてしまいます。
最近は使用されるケースが激減しているので、若いフロントエンドエンジニアだと知らずに使ってしまう可能性が高くなります。
function App() {
// 印刷画面を開いてしまう
return <button onClick={print}>button</button>
}
export default App
alert
アラートを開いてしまいます。
function App() {
// アラートを開いてしまう
return <button onClick={alert}>button</button>
}
export default App
confirm
メッセージのないダイアログを表示してしまいます。
TypeScriptを使用している場合は引数なしだと警告が出るので気づきやすいです。
function App() {
// ダイアログを表示してしまう
return <button onClick={confirm}>button</button>
}
export default App
prompt
confirmと同様にメッセージのないダイアログを表示してしまいます。
こちらもTypeScriptを使用している場合は引数なしだと警告が出るので気づきやすい。
function App() {
// ダイアログを表示してしまう
return <button onClick={prompt}>button</button>
}
export default App
blur
onClickイベントで使用した場合は特に何も起きないことが多い。
何も起きず、TypeScriptの警告も表示されないのでコードで使用されても気づきにくい。
function App() {
// クリックしても特に何も起きない
return <button onClick={blur}>button</button>
}
export default App
focus
onClickイベントで使用した場合は特に何も起きないことが多い。
何も起きず、TypeScriptの警告も表示されないのでコードで使用されても気づきにくい。
function App() {
// クリックしても特に何も起きない
return <button onClick={focus}>button</button>
}
export default App
まとめ
JavaScriptには組み込み関数があり、同じ名前の関数を間違えて使用してしまうと気づかずにバグを組み込んでしまう可能性があるのでESLintでno-restricted-globalsを9つ追加しておくと安全です。
['alert', 'blur', 'close', 'confirm', 'focus', 'open', 'print', 'prompt', 'stop']
JavaScriptの組み込み関数はこれら以外にもたくさんあるのですが、const close = () => で定義したものを消したあとにonClickイベントなどに入れたままにしてしまう可能性が高いものは、私が調べた範囲ではこの9つだけです。
これらを人力で視認してコード内に使用されている箇所がないか確認するのは厳しいので、ESLintを設定して自動的に検出できるようにすると良いでしょう。