useEffectフックで状態を更新する:useState、useRef、useReducerとの比較
useEffectフック内で状態を設定できる?
なぜ直接設定できないのか?
useEffectフックは、コンポーネントのレンダリング後に副作用を実行するために使用されます。副作用とは、APIからのデータ取得、タイマーの設定、DOM操作など、コンポーネントの状態を変更する処理を指します。
一方、状態はコンポーネント内部のデータであり、直接変更するとレンダリングがトリガーされます。useEffectフック内で直接状態を変更してしまうと、レンダリングループが発生してしまう可能性があります。
状態を更新する方法
useState
フックを使用して状態を管理し、useEffect
フック内でその状態更新関数を呼び出す方法です。
const [count, setCount] = useState(0);
useEffect(() => {
// 1秒後に状態を更新
setTimeout(() => {
setCount(count + 1);
}, 1000);
}, []);
useRef
フックを使用して可変参照を作成し、その参照の .current
プロパティを介して状態を更新する方法です。
const countRef = useRef(0);
useEffect(() => {
// 1秒後に状態を更新
setInterval(() => {
countRef.current++;
}, 1000);
}, []);
useEffect フック内で別のコンポーネントの状態を更新する
状態を更新したいコンポーネントを別のコンポーネントとしてラップし、ラップされたコンポーネントの状態を更新する方法です。
const ParentComponent = () => {
const [count, setCount] = useState(0);
return (
<ChildComponent count={count} setCount={setCount} />
);
};
const ChildComponent = ({ count, setCount }) => {
useEffect(() => {
// 1秒後に状態を更新
setTimeout(() => {
setCount(count + 1);
}, 1000);
}, []);
return (
<div>
カウント: {count}
</div>
);
};
useEffectフック内で直接状態を設定することはできませんが、上記のような方法で間接的に状態を更新することは可能です。状況に応じて適切な方法を選択してください。
useState フックを使用する
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
// 1秒後に状態を更新
setTimeout(() => {
setCount(count + 1);
}, 1000);
}, []);
return (
<div>
カウント: {count}
</div>
);
};
useRef フックを使用する
const App = () => {
const countRef = useRef(0);
useEffect(() => {
// 1秒後に状態を更新
setInterval(() => {
countRef.current++;
}, 1000);
}, []);
return (
<div>
カウント: {countRef.current}
</div>
);
};
このコードでは、useRef
フックを使用して countRef
という可変参照を作成しています。useEffect
フック内で、setInterval
を使用して 1秒後に countRef.current
を 1 増加させる処理を実行しています。
useEffect フック内で別のコンポーネントの状態を更新する
const ParentComponent = () => {
const [count, setCount] = useState(0);
return (
<ChildComponent count={count} setCount={setCount} />
);
};
const ChildComponent = ({ count, setCount }) => {
useEffect(() => {
// 1秒後に状態を更新
setTimeout(() => {
setCount(count + 1);
}, 1000);
}, []);
return (
<div>
カウント: {count}
</div>
);
};
const App = () => {
return (
<ParentComponent />
);
};
このコードでは、ParentComponent
と ChildComponent
という 2 つのコンポーネントを作成しています。ParentComponent
は count
という状態変数を持ち、ChildComponent
にその状態と状態更新関数を props として渡しています。ChildComponent
は useEffect
フック内で count
を 1 秒後に 1 増加させる処理を実行しています。
動作確認
上記のコードをそれぞれファイルに保存し、npm start
コマンドを実行してブラウザで開くと、カウントが 1 秒ごとに増加することを確認できます。
useEffectフック内で状態を更新するその他の方法
useReducer
フックは、状態更新ロジックをより複雑な場合に管理するために使用できます。
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
};
const App = () => {
const [count, dispatch] = useReducer(reducer, 0);
useEffect(() => {
// 1秒後に状態を更新
setTimeout(() => {
dispatch({ type: 'INCREMENT' });
}, 1000);
}, []);
return (
<div>
カウント: {count}
</div>
);
};
状態更新関数を直接渡す
useEffect
フックの第1引数に、状態更新関数を含むオブジェクトを渡す方法です。
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
// 1秒後に状態を更新
const interval = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<div>
カウント: {count}
</div>
);
};
カスタムフックを作成する
上記の方法を組み合わせたカスタムフックを作成し、コードを再利用する方法です。
const useCounter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
// 1秒後に状態を更新
const interval = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return { count, setCount };
};
const App = () => {
const { count, setCount } = useCounter();
return (
<div>
カウント: {count}
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
};
useEffectフック内で状態を更新する方法はいくつかあります。状況に応じて適切な方法を選択してください。
javascript reactjs react-hooks