この記事は、ラクスパートナーズ AdventCalendar 2025の17日目の記事です。
(個人で25日連続投稿にチャレンジ中のカレンダーになります)
Reactにおいて再レンダリングが発生する条件と、不要な再レンダリングを防止する方法をまとめてみました。
再レンダリングが起きる条件
stateが変更された時
コンポーネントでstateが変更されると、そのコンポーネントは再レンダリングします。
import { useState } from 'react'
const Profile = () => {
// nameの値が変わるとProfileコンポーネントも更新される
const [name, setName] = useState('')
return (
// 省略
)
}
親コンポーネントが再レンダリングされた時
親コンポーネントが再レンダリングされると、子コンポーネントも再レンダリングされます。
import { useState } from 'react'
const Parent = () => {
// titleの値が変わるとParentコンポーネントが再レンダリングされる
const [title, setTitle] = useState('')
return (
// titleの値が変わるとChildコンポーネントも再レンダリングされる
<Child />
)
}
※ 「propsが変更された時、子コンポーネントも再レンダリングされる」という話を聞いたのですが、厳密にはpropsは関係ないそうですね。あくまで親コンポーネントが再レンダリングされるから、それに伴って子コンポーネントも再レンダリングされる、ということでした。
(この後触れる、Reactのmemoという機能を使うと話は別です)
対策
Reactのmemoでメモ化する
memoを使うと、親コンポーネントで再レンダリングが起きても、propsに変更がなければ子コンポーネントが再レンダリングされなくなります。
これをメモ化と言います。
import { useState } from 'react'
const Parent = () => {
// titleの値が変わるとParentコンポーネントが再レンダリングされる(Childコンポーネントは再レンダリングされない)
const [title, setTitle] = useState('')
// textの値が変わるとChildコンポーネントも再レンダリングされる
const [text, setText] = useState('')
return (
<>
<div>{ title }</div>
<Child text={text} />
</>
)
}
import { memo } from 'react'
const Child = memo((props) => {
const { text } = props;
return (
// 省略
)
})
肥大化していきそうなコンポーネントには、memoを使って積極的にメモ化してあげると良さそうですね。
useCallbackを使用する
子コンポーネントに親コンポーネントで定義したアロー関数を渡すと、
「毎回新しい関数を生成している」
という判断をされてしまうそうです。
その結果、親コンポーネントで再レンダリングが発生する度に、子コンポーネントも再レンダリングされてしまいます。
(そのため、memoを使ってメモ化していても防げない)
const Parent = () => {
// titleの値が変わるとParentコンポーネントが再レンダリングされる(Childコンポーネントは再レンダリングされない)
const [title, setTitle] = useState('')
// textの値が変わるとChildコンポーネントも再レンダリングされる
const [text, setText] = useState('')
const [isShow, setIsShow] = useState('')
// 毎回新しいアロー関数が渡されてしまう
const onClickClose = () => setIsShow(false)
return (
<>
<div>{ title }</div>
<Child text={text} isShow={isShow} onClickClose={onClickClose} />
</>
)
}
const Child = memo((props) => {
const { text, isShow, onClickClose } = props;
return (
{isShow ? (
{/* isShowをfalseにするボタン */}
<button onClick={onClickClose}></button>
// 省略()
) : null}
)
})
これを防ぐのに役立つのがuseCallbackです。
useCallbackを使うことで、指定した関数の再生成を制御して、不要なレンダリングを防ぐことができます。
import { useState, useCallback } from 'react'
const Parent = () => {
// titleの値が変わるとParentコンポーネントが再レンダリングされる(Childコンポーネントは再レンダリングされない)
const [title, setTitle] = useState('')
// textの値が変わるとChildコンポーネントも再レンダリングされる
const [text, setText] = useState('')
const [isShow, setIsShow] = useState('')
const onClickClose = () => useCallback(setIsShow(false), [])
return (
<>
<div>{ title }</div>
{/* 子コンポーネントに関数渡すと */}
<Child text={text} isShow={isShow} onClickClose={onClickClose} />
</>
)
}
useCallback(関数, 監視する変数)のような形式で使うことができます。監視対象の値が変わると、第一引数の関数を再生成します。
useEffect()と同じように、第二引数部分に空の配列を指定することができて、空の配列を指定すると関数の再生成を防ぐことができます。
これにより、子コンポーネントの不要な再レンダリングも防止できるようです。
「ダイアログのコンポーネントを呼び出して、ダイアログ側で表示フラグをfalseにして閉じる」みたいなことをすると再レンダリングさせてしまいそうですね。気をつけねば。
参考
参考にさせていただいた記事です。
ありがとうございました。
https://qiita.com/yokoto/items/ee3ed0b3ca905b9016d3