
Reactで状態を扱う場面では、useStateやuseEffectの他に、再描画を伴わずに値を保持したいケースがあります。そのようなときに使われるのが、useRefです。
今回は、あるシステムで見られた「constで定義したuseRefに対して値を代入してもエラーにならないのはなぜか?」という疑問を出発点に、useRefの正しい使い方と注意点を整理します。
constで定義した変数に代入?エラーにならない理由とは
以下のようなコードを見たとき、不思議に思った方もいるかもしれません。
const endDtExtendedDays = useRef(0); endDtExtendedDays.current = coupon.end_dt_min_interval_days;
一見すると、constで定義された変数に代入しているように見えますが、実はこれは参照先のプロパティを書き換えているだけであり、endDtExtendedDays自体の参照は変更されていないため、エラーにはなりません。
つまり、useRefの戻り値は以下のような構造を持っています。
{
current: 任意の値
}
このcurrentプロパティは自由に読み書きでき、再描画のきっかけとはならないのが特徴です。
useRefが有効な場面とは
useRefは、以下のような目的でよく使われます。
- 状態の保持(ただしUI更新の必要がないもの)
- 処理中フラグの管理
- 以前の値の保持
- タイマーIDや外部インスタンスの参照
たとえば、ボタン連打による処理の重複を防止する際には、以下のような使い方が有効です。
const processing = useRef(false);
const handleClick = () => {
if (processing.current) return;
processing.current = true;
// 処理を実行
setTimeout(() => {
processing.current = false;
}, 1000);
};
useEffectと連携する際の注意点
useRefの値は変更しても再描画が発生しないため、useEffectなどの副作用フック内で値を使う場合には注意が必要です。
以下のような実装例がありました。
useEffect(() => {
if (startDt) {
const now = new Date();
const daysDiff = Math.abs(differenceInDays(now, startDt));
const min = daysDiff <= 30
? addDays(now, endDtExtendedDays.current)
: startDt;
setMinDate(min);
}
}, [startDt]);
このコードでは、endDtExtendedDays.currentの変更は依存配列に含まれていないため、変更後も再評価されません。
解決策
再描画に反映させたい場合は、useStateと組み合わせて以下のようにすると確実です。
const [endDtExtendedDays, setEndDtExtendedDays] = useState(0);
useEffect(() => {
if (startDt) {
const now = new Date();
const isWithinRange = Math.abs(differenceInDays(now, startDt)) <= 30;
const min = isWithinRange
? addDays(now, endDtExtendedDays)
: startDt;
setMinDate(min);
}
}, [startDt, endDtExtendedDays]);
useEffect(() => {
const load = async () => {
const data = await fetchCoupon(couponId);
setEndDtExtendedDays(data.end_dt_min_interval_days);
};
load();
}, []);
このようにすると、endDtExtendedDaysが変わった時点で再評価が行われるようになります。
よくある誤解とアンチパターン
useRefを使えば便利だからといって、全ての状態をこれで管理するのはおすすめできません。
特に次のような場合は避けるべきです。
- UIに直接関係する値の保持 →
useStateを使うべきです。 - 副作用に関係する値をuseRefだけで扱う →
useEffectとの組み合わせに注意が必要です。
まとめ
ReactにおけるuseRefの特徴を理解すれば、再描画の無駄を避けつつ、状態を効率よく保持することができます。
- constで定義しても、
useRefのcurrentは変更可能 - currentの変更は再描画されない
useEffectとの併用時は依存配列に注意- UIと連動する値にはuseStateを使うべき
今回のようなシステム内の不具合や疑問点は、同じようにReactを利用している多くのエンジニアにとっても身近な問題です。
Reactの特性を正しく理解しておくことで、より信頼性の高いフロントエンド実装が可能になります。
- Original:https://minory.org/react-useref.html
- Source:minory
- Author:管理者