Reactでconsole.logが2回実行される
ReactではApp.tsx内などでconsole.logが2回実行されることがあります。
例えば以下のコマンドでReactの環境を構築したあと、App.tsxのApp()内にconsole.log(count)を記述して実行すると、2回実行されていることが確認できます。
npm create vite@latest my-react-ts -- --template react-swc-ts
function App() {
const [count, setCount] = useState(0)
console.log(count)
// 中略
}
2回実行される原因は<StrictMode>
<StrictMode>は開発環境においてコンポーネントの一般的なバグを早期に見つけるのに役立ちます。
Strict Modeは常にレンダー関数を2回呼び出すため、console.logも2回実行されます。
逆に言うとStrict Modeでなければ、レンダー関数の実行は1回なので、main.tsx内の<React.StrictMode>を削除すれば、console.logの実行も1回になります。
ReactDOM.createRoot(document.getElementById('root')!).render(
/*<React.StrictMode>
<App />
</React.StrictMode>*/
<App />
)
しかし、最初に書いたように<StrictMode>はバグを早期に見つけるためのものなので、<StrictMode>を削除するのは好ましくないです。
そのため、この記事では他の2つの方法について説明します。
React Developer Toolsをインストールする
React Developer Toolsはブラウザの拡張機能で、Reactのコンポーネントを調査し、propsやstateを編集し、パフォーマンスの問題を特定できます。
React Developer Toolsをインストールしていると、Strict ModeによるConsoleの2回目の結果はグレー色で表示されるので、違いがわかりやすくなります。
consoleOnce関数を作成する
console.logの引数が同じ場合は実行しない、consoleOnce関数を作成すると、console.logを2回実行させないことができます。
関数の作り方は、引数を貯めるための配列を作成して、初めて取得した値なら配列に入れて、すでに配列にあればconsole.logを実行しないようにします。
ネット上にはisConsole: booleanのような判定でconsole.logを1回だけ実行しているコードもありますが、以下のように関数にしたほうが使い回せて便利です。
const consoleExecuted: any[] = []
function consoleOnce(val: any): void {
if (!consoleExecuted.includes(val)) {
console.log(val)
consoleExecuted.push(val)
}
}
function App() {
const [count, setCount] = useState(0)
consoleOnce(count)
// 中略
}
例として以下のようなコードも書きました。
引数の値が同じfuncA(10)は1回だけ実行されていることがわかります。
const consoleExecuted: any[] = []
function consoleOnce(val: any): void {
if (!consoleExecuted.includes(val)) {
console.log(val)
consoleExecuted.push(val)
}
}
function Sample() {
const funcA = (num: number): void => {
consoleOnce(num)
}
const funcB = (num: number): void => {
consoleOnce(num * 2)
}
const funcC = (str: string): void => {
consoleOnce(str + ' world!')
}
funcA(10)
funcB(7)
funcA(10)
funcC('Hello')
return <h1>Sample</h1>
}
export default Sample
もし同じ引数のconsole.logを実行したい場合は、引数にidを追加して実行できるように機能を拡張すると良いでしょう。
const consoleExecuted: any[] = []
function consoleOnce(val: any, id?: number): void {
if (!consoleExecuted.includes(`${id}:${val}`)) {
console.log(val)
consoleExecuted.push(`${id}:${val}`)
}
}
function Sample() {
const funcA = (num: number, id: number): void => {
consoleOnce(num, id)
}
const funcB = (num: number): void => {
consoleOnce(num * 2)
}
const funcC = (str: string): void => {
consoleOnce(str + ' world!')
}
funcA(10, 1)
funcB(7)
funcA(10, 2)
funcC('Hello')
return <h1>Sample</h1>
}
export default Sample