質問するログイン新規登録

Q&A

解決済

3回答

523閲覧

Reactのコンポーネントの不必要な再レンダリングを防止したい

vj2a6wk5

総合スコア22

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2026/02/21 12:13

編集2026/02/23 07:16

0

0

実現したいこと

Reactのコンポーネントの不必要な再レンダリングを防止したいです。

発生している問題

name, password どちらか1文字でも値を変更するとApp自体が再レンダリングされてしまいます。

Reactのコンポーネントの再レンダリングが発生しているかは React Developer Tools を利用して視覚的に確認しています。

該当のソースコード

jsx

1import { useState } from 'react' 2 3export default function App() { 4 const [name, setName] = useState('') 5 const [password, setPassword] = useState('') 6 7 function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) { 8 e.preventDefault() 9 10 console.log(`name is ${name}`) 11 console.log(`password is ${password}`) 12 } 13 14 return ( 15 <form onSubmit={handleSubmit}> 16 <div> 17 <input value={name} onChange={(e) => { setName(e.target.value) }} /> 18 </div> 19 20 <div> 21 <input value={password} onChange={(e) => { setPassword(e.target.value) }} /> 22 </div> 23 24 <input type="submit" /> 25 </form> 26 ) 27}

試したこと1

以下のように NameInput と PasswordInput に別けました。
コンポーネントごとの最小限の再レンダリングなので期待する結果になりました。
しかし、 handleSubmit で name と password が取得できなくなりました。

tsx

1import { useState } from 'react' 2 3function NameInput() { 4 const [name, setName] = useState('') 5 6 return ( 7 <div> 8 <input name="name" value={name} onChange={(e) => setName(e.target.value)} /> 9 </div> 10 ) 11} 12 13function PasswordInput() { 14 const [password, setPassword] = useState('') 15 16 return ( 17 <div> 18 <input name="password" value={password} onChange={(e) => setPassword(e.target.value)} /> 19 </div> 20 ) 21} 22 23export default function App() { 24 function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) { 25 e.preventDefault() 26 27 console.log(`name is ${name}`) 28 console.log(`password is ${password}`) 29 } 30 31 return ( 32 <form onSubmit={handleSubmit}> 33 <NameInput /> 34 <PasswordInput /> 35 <input type="submit" /> 36 </form> 37 ) 38}

試したこと2

useState が原因で再レンダリングされているので、そもそも useState を利用しないようにしました。
handleSubmit で name, password の値が取得できていますが React として適切な書き方なのかが疑問です。
どちらかというと今までの古き良きHTML + JavaScriptのような書き方を彷彿とさせます。
この書き方で問題ないならそもそも useState の存在理由も疑問に思えてきます。

また、useEffect であればエディタの名前変更機能を利用すれば自動的に全ての変数名が変更されますが、FormDataの場合は必ず手動でname属性と formData.get を修正する必要があります。
フォームが複雑になると人為的ミスをする可能性も出てくるので避けたいです。

tsx

1export default function App() { 2 function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) { 3 e.preventDefault() 4 5 const formData = new FormData(e.currentTarget) 6 7 const name = formData.get('name') 8 const password = formData.get('password') 9 10 console.log(`name is ${name}`) 11 console.log(`password is ${password}`) 12 } 13 14 return ( 15 <form onSubmit={handleSubmit}> 16 <div> 17 <input name="name" /> 18 </div> 19 <div> 20 <input name="password" /> 21 </div> 22 <input type="submit" /> 23 </form> 24 ) 25}

試したこと3

memo を利用してメモ化しましたが name, password どちらか1方を変更すると、どちらも再レンダリングされてしまいます。

tsx

1import { memo, useState } from 'react' 2 3interface Props { 4 value: string 5 setValue: (value: string) => void 6} 7 8function NameInput(props: Props) { 9 return ( 10 <div> 11 <input name="name" value={props.value} onChange={(e) => props.setValue(e.target.value)} /> 12 </div> 13 ) 14} 15 16function PasswordInput(props: Props) { 17 return ( 18 <div> 19 <input name="password" value={props.value} onChange={(e) => props.setValue(e.target.value)} /> 20 </div> 21 ) 22} 23 24const MemoNameInput = memo(NameInput) 25const MemoPasswordInput = memo(PasswordInput) 26 27export default function App() { 28 const [name, setName] = useState('') 29 const [password, setPassword] = useState('') 30 31 function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) { 32 e.preventDefault() 33 34 console.log(`name is ${name}`) 35 console.log(`password is ${password}`) 36 } 37 38 return ( 39 <form onSubmit={handleSubmit}> 40 <MemoNameInput value={name} setValue={(value) => { setName(value) }} /> 41 <MemoPasswordInput value={password} setValue={(value) => { setPassword(value) }} /> 42 43 <input type="submit" /> 44 </form> 45 ) 46}

試したこと4

useCallback を利用して「試したこと3」を改良しました。
値を変更すると「AppとNameInput」または「AppとPasswordInput」のように個別にレンダリングできるようになりました。
Appが再レンダリングされる事は App 内に useState が存在するので仕方ないようです。
しかし、たった2つのinputですが、これだけ大量のコードを書かなくてはいけないのでしょうか?
とても大変です。なにか良い方法はないですか?

tsx

1import { memo, useCallback, useState } from 'react' 2 3interface Props { 4 value: string 5 setValue: (value: string) => void 6} 7 8function NameInput(props: Props) { 9 return ( 10 <div> 11 <input name="name" value={props.value} onChange={(e) => props.setValue(e.target.value)} /> 12 </div> 13 ) 14} 15 16function PasswordInput(props: Props) { 17 return ( 18 <div> 19 <input name="password" value={props.value} onChange={(e) => props.setValue(e.target.value)} /> 20 </div> 21 ) 22} 23 24const MemoNameInput = memo(NameInput) 25const MemoPasswordInput = memo(PasswordInput) 26 27export default function App() { 28 const [name, setName] = useState('') 29 const [password, setPassword] = useState('') 30 31 function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) { 32 e.preventDefault() 33 34 console.log(`name is ${name}`) 35 console.log(`password is ${password}`) 36 } 37 38 const handleSetName = useCallback((value: string) => { 39 setName(value) 40 }, []) 41 42 const handleSetPassword = useCallback((value: string) => { 43 setPassword(value) 44 }, []) 45 46 return ( 47 <form onSubmit={handleSubmit}> 48 <MemoNameInput value={name} setValue={handleSetName} /> 49 <MemoPasswordInput value={password} setValue={handleSetPassword} /> 50 51 <input type="submit" /> 52 </form> 53 ) 54}

試したこと5

setValue に直接 setter を指定して「試したこと3」を改良しました。
関数オブジェクトが再生成されないようになりました。

tsx

1import { memo, useState } from 'react' 2 3interface Props { 4 value: string 5 setValue: (value: string) => void 6} 7 8function Input(props: Props) { 9 return ( 10 <div> 11 <input value={props.value} onChange={(e) => props.setValue(e.target.value)} /> 12 </div> 13 ) 14} 15 16const MemoInput = memo(Input) 17 18export default function App() { 19 const [name, setName] = useState('') 20 const [password, setPassword] = useState('') 21 22 function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) { 23 e.preventDefault() 24 25 console.log(`name is ${name}`) 26 console.log(`password is ${password}`) 27 } 28 29 return ( 30 <form onSubmit={handleSubmit}> 31 <MemoInput value={name} setValue={setName} /> 32 <MemoInput value={password} setValue={setPassword} /> 33 34 <input type="submit" /> 35 </form> 36 ) 37}

試したこと6

「試したこと5」の memo を利用する必要がないことに気付いたので修正しました。

tsx

1import { useState } from 'react' 2 3export default function App() { 4 const [name, setName] = useState('') 5 const [password, setPassword] = useState('') 6 7 function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) { 8 e.preventDefault() 9 10 console.log(`name is ${name}`) 11 console.log(`password is ${password}`) 12 } 13 14 function handleChangeName(e: React.ChangeEvent<HTMLInputElement>) { 15 setName(e.target.value) 16 } 17 18 function handleChangePassword(e: React.ChangeEvent<HTMLInputElement>) { 19 setPassword(e.target.value) 20 } 21 22 return ( 23 <form onSubmit={handleSubmit}> 24 <div> 25 <input name="name" value={name} onChange={handleChangeName} /> 26 </div> 27 <div> 28 <input name="password" value={password} onChange={handleChangePassword} /> 29 </div> 30 31 <input type="submit" /> 32 </form> 33 ) 34}

試したこと7

handleChangeName と handleChangePassword を共通化しました。
name の指定が少し長くなっていますが型安全の為です。仕方ないです。

tsx

1import { useState } from 'react' 2 3export default function App() { 4 const [form, setForm] = useState({ 5 name: '', 6 password: '' 7 }) 8 9 type FormKeys = keyof typeof form 10 11 function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) { 12 e.preventDefault() 13 14 console.log(`name is ${form.name}`) 15 console.log(`password is ${form.password}`) 16 } 17 18 function handleChange(e: React.ChangeEvent<HTMLInputElement>) { 19 setForm({ ...form, [e.target.name]: e.target.value }) 20 } 21 22 return ( 23 <form onSubmit={handleSubmit}> 24 <div> 25 <input name={"name" satisfies FormKeys} value={form.name} onChange={handleChange} /> 26 </div> 27 <div> 28 <input name={"password" satisfies FormKeys} value={form.password} onChange={handleChange} /> 29 </div> 30 31 <input type="submit" /> 32 </form> 33 ) 34}

補足情報(FW/ツールのバージョンなど)

  • React 19.2.4

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

guest

回答3

0

「試したこと2」は、React 公式ドキュメントの以下の章にあるコード例と同じ方向性なので、それで全く問題ないと思います。

型チェックのための仕組みを追加で導入するのは良いことだと思います。

https://ja.react.dev/reference/react-dom/components/form#handle-form-submission-on-the-client

jsx

1// ドキュメントにあるコード例 2export default function Search() { 3 function search(formData) { 4 const query = formData.get("query"); 5 alert(`You searched for '${query}'`); 6 } 7 return ( 8 <form action={search}> 9 <input name="query" /> 10 <button type="submit">Search</button> 11 </form> 12 ); 13}

投稿2026/02/24 15:32

編集2026/02/24 15:35
honey32

総合スコア252

0

自己解決

「試したこと7」の方法で解決しました。

tsx

1import { useState } from 'react' 2 3export default function App() { 4 const [form, setForm] = useState({ 5 name: '', 6 password: '' 7 }) 8 9 type FormKeys = keyof typeof form 10 11 function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) { 12 e.preventDefault() 13 14 console.log(`name is ${form.name}`) 15 console.log(`password is ${form.password}`) 16 } 17 18 function handleChange(e: React.ChangeEvent<HTMLInputElement>) { 19 setForm({ ...form, [e.target.name]: e.target.value }) 20 } 21 22 return ( 23 <form onSubmit={handleSubmit}> 24 <div> 25 <input name={"name" satisfies FormKeys} value={form.name} onChange={handleChange} /> 26 </div> 27 <div> 28 <input name={"password" satisfies FormKeys} value={form.password} onChange={handleChange} /> 29 </div> 30 31 <input type="submit" /> 32 </form> 33 ) 34}

投稿2026/02/23 07:19

vj2a6wk5

総合スコア22

0

ちゃんと実験されていて尊敬です・・!見習いたい・・
試したこと1から順に回答していきます。

しかし、 handleSubmit で name と password が取得できなくなりました。

親(App)でもっていたstateが子(NameInput, PasswordInput)に移ったため、<App />のhandleSubmitでは参照できなくなります。

この書き方で問題ないならそもそも useState の存在理由も疑問に思えてきます。

useStateを使わない書き方の場合、入力値はReactの制御下にないため、入力中のバリデーションやパスワードの強度メーターを入力値と連動して行うなどができなくなります。一方で、そういった要件がなければこの書き方で問題ないと思います。
質問者さんのおっしゃる通り、useStateを使っていると入力値が更新されるたびに再レンダリングが発生します。

制御コンポーネントについてはこちらが参考になります。

memo を利用してメモ化しましたが name, password どちらか1方を変更すると、どちらも再レンダリングされてしまいます。

例えば

setValue={(value) => { setName(value) }}

こちらは、レンダリングされるたびに新しい関数オブジェクトを生成します。
React.memoはshallow comparisonを行うため、propsが変わると再レンダリングが走ります。
こちらの記事参考になるのでぜひ)

しかし、たった2つのinputですが、これだけ大量のコードを書かなくてはいけないのでしょうか?

書かなくて大丈夫です。
まず、質問者さんのアプローチは関数定義の参照をuseCallbackでキャッシュしたことで、試したこと3で起きていた関数オブジェクトが毎回新しく生成される問題を解消していましたが、そもそも毎回新しく関数オブジェクトを生成しないようにすればいいのです。つまり、無名関数を渡しているところをsetter(setName, setPassword)を渡すようにすることで解消します。

投稿2026/02/23 06:18

ymmr

総合スコア136

vj2a6wk5

2026/02/23 07:18

ありがとうございます。質問本文に試したこと5,6,7を追記しました。 「試したこと7」がコード行数も多くなく、おそらくこれ以上の改善は難しいと感じます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.29%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問