結論
React の useState
フックを使用する際、関数を状態として保持したいケースでは注意が必要です。
どんなコードだとまずいか
直接関数を useState
に渡すと、意図せぬ挙動になる場合があります。
具体的には、
- useState で, 値ではなく関数を保持したいとき
です. 以下のようなコードは避けるべきです。
const [stateFunction, setStateFunction] = useState(myFunction);
このコードでは、myFunction は初期状態を計算するために一度だけ実行され、その戻り値が state の初期値となります。これは、myFunction を関数として state に保持したい場合には適していません。
どのように実装すべきか
関数を state として保持する正しい方法は、その関数を返す別の関数にラップして useState に渡すことです。
const [stateFunction, setStateFunction] = useState(() => myFunction);
この形式では、myFunction は state の値として保持され、実行されません。これにより、関数を状態として適切に管理できます。
理由
useState は、初期状態を計算するために関数を一度だけ実行します。そのため、関数を直接渡すと、その関数は初期状態を生成するために使用され、関数そのものが state に保持されるわけではありません。これは、関数を状態として使用したい場合には望ましくありません。
ラップされた関数を使用することで、React はそのラップされた関数を実行し、その戻り値(この場合は myFunction)を初期状態として使用します。これにより、関数そのものが状態として保持されます。
より具体的に
誤った実装例
import React, { useState } from 'react';
function MyComponent() {
const myFunction = () => {
console.log("Function called");
return "Hello World";
};
// ❌: myFunction は関数としては渡されず, myFunction の実行結果が渡されることになる
const [stateFunction, setStateFunction] = useState(myFunction);
console.log(stateFunction);
// Log: "Hello World"
// functin の情報が表示されると思いきや, myFunctionの実行結果が表示される
return (
<div>
{/* Rest of the component */}
</div>
);
}
正しい実装例
import React, { useState } from 'react';
function MyComponent() {
const myFunction = () => {
console.log("Function called");
return "Hello World";
};
// ⭕️: myFunction を 返す関数を渡す
const [stateFunction, setStateFunction] = useState(() => myFunction);
console.log(stateFunction);
// log: myFunction の関数としての情報が出力される
return (
<div>
{/* Rest of the component */}
</div>
);
}
まとめ
React の useState フックを使用して関数を状態として扱う場合は、関数を直接渡すのではなく、それを返す別の関数にラップして渡す必要があります。この方法により、関数は状態として正しく保持され、意図したとおりに機能します。