質問編集履歴

2

修正後のコードと、新たな問題点を追記

2021/07/06 12:46

投稿

tksx1227
tksx1227

スコア6

test CHANGED
File without changes
test CHANGED
@@ -36,6 +36,12 @@
36
36
 
37
37
 
38
38
 
39
+ **追記**
40
+
41
+ ある程度修正したコードを下の方に追加しています。
42
+
43
+
44
+
39
45
  ### 該当のソースコード
40
46
 
41
47
  Parent.jsx(親コンポーネント)
@@ -161,3 +167,203 @@
161
167
 
162
168
 
163
169
  ```
170
+
171
+
172
+
173
+
174
+
175
+ ### 追加したところ
176
+
177
+ 以下が修正後のコードです。
178
+
179
+ まだ問題点があるのですが、一応、修正した箇所とその意図を説明します。
180
+
181
+ ###### 其の一
182
+
183
+ まずは、mapで回す際のkeyについては、onClick関数内で、**リストに追加する要素の初期値を作成する際に、v4を用いてオブジェクトに一意の値を付与し、それをkeyとすること**で対処しました。
184
+
185
+
186
+
187
+ これは動的に要素を削除することを見越して、keyにidxを設定することを避けるという意図があります。
188
+
189
+
190
+
191
+ ###### 其の二
192
+
193
+ 次に、maisumakunさんにご指摘いただいたように、setItem={(item) => setItem(idx, item)}の個所で関数を再定義してしまっていたため、こちらをsetItem={setItem}に変更し、さらにidx={idx}としてpropsとして添え字を別途子側に渡すようにしました。
194
+
195
+
196
+
197
+ 子側でidxをsetItemの引数に渡すことで、修正前と同様の挙動をしてくれます。
198
+
199
+
200
+
201
+ ###### 其の三
202
+
203
+ 最後にsetItem関数の変更について説明します。
204
+
205
+
206
+
207
+ setItem関数は、無駄な再レンダリングを防ぐためにuseCallbackで囲っていますが、どうやら第二引数を空リストにしていると、**関数定義時に参照した値が内部に保持される**らしく、そちらを改善しました(改善しきれていなかったので、下に追加で色々書いてます)。
208
+
209
+
210
+
211
+ まず上記の意味について説明します。
212
+
213
+
214
+
215
+ 最初のレンダリングにてsetItemが定義されたとき、listの値は[](空リスト)となっています。
216
+
217
+ そのため、setItem内部にある const newList = [...list]; において、listの値に[](空リスト)が保持されたままの状態になっていました。
218
+
219
+
220
+
221
+ つまり、**子側でsetItemを実行したとき、実行したタイミングでその時のlistの値を参照しに行くのではなく、最初に保持していた[](空リスト)が使われてしまい、うまく機能しない状態だった**ということです(もしかしたら当たり前のことかも)。
222
+
223
+
224
+
225
+ 個人的には、要素を追加するボタンを押したタイミングでlistが更新されれば十分だったため、新たにボタンを押したときに値が変更されるstateを追加し、それをuseCallbackの第二引数に指定することで、上記の問題を解決することができました(できてなかった)。
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+ **本来この文章を書いて終わりにしようと思っていたのですが、このuseCallbackの個所にまだ問題点がありました。結局、useCallback内の関数が更新される前に追加した要素すべてが、listの初期値である[](空リスト)を所持しており、連続で要素を追加するとおかしな挙動を取るっぽいです、、**
234
+
235
+ **そんなわけで、useCallback内の関数が実行されたタイミングでlistの値が更新できたらいいな、と考えています。**
236
+
237
+
238
+
239
+
240
+
241
+ ちなみに、第二引数を[list]とした場合、入力中以外のinputタグまで再レンダリングされてしまったため、こちらは没にしました。
242
+
243
+ ### 差分を掲載
244
+
245
+ Parent.jsx(親コンポーネント)
246
+
247
+ ```diff
248
+
249
+ import { useState, useCallback } from "react";
250
+
251
+ import { Child } from "./Child";
252
+
253
+ + import { v4 } from "uuid";
254
+
255
+
256
+
257
+ export const Parent = () => {
258
+
259
+ console.log("Parent");
260
+
261
+ const [list, setList] = useState([]);
262
+
263
+ + const [count, setCount] = useState(0);
264
+
265
+
266
+
267
+ const setItem = useCallback((idx, item) => {
268
+
269
+ const newList = [...list];
270
+
271
+ newList[idx] = item;
272
+
273
+ setList(newList);
274
+
275
+ - }, []);
276
+
277
+ + }, [count]);
278
+
279
+
280
+
281
+ const onClick = () => {
282
+
283
+ const newList = [
284
+
285
+ ...list,
286
+
287
+ {
288
+
289
+ + key: v4(),
290
+
291
+ param1: "", // この値を子コンポーネントで埋めたい
292
+
293
+ param2: "" // この値を子コンポーネントで埋めたい
294
+
295
+ }
296
+
297
+ ];
298
+
299
+ setList(newList);
300
+
301
+ + setCount((prevState) => prevState + 1);
302
+
303
+ };
304
+
305
+
306
+
307
+ return (
308
+
309
+ <>
310
+
311
+ {list.map((item, idx) => {
312
+
313
+ return (
314
+
315
+ - <Child key={idx} item={item} setItem={(item) => setItem(idx, item)} />
316
+
317
+ + <Child
318
+
319
+ + key={item.key}
320
+
321
+ + item={item}
322
+
323
+ + idx={idx}
324
+
325
+ + setItem={setItem}
326
+
327
+ + />
328
+
329
+ );
330
+
331
+ })}
332
+
333
+ <button onClick={onClick}>追加</button>
334
+
335
+ </>
336
+
337
+ );
338
+
339
+ };
340
+
341
+
342
+
343
+ ```
344
+
345
+
346
+
347
+ Child.jsx(子コンポーネント)
348
+
349
+ ```diff
350
+
351
+ import React, { memo } from "react";
352
+
353
+
354
+
355
+ - export const Child = memo(({ item, setItem }) => {
356
+
357
+ + export const Child = memo(({ item, setItem, idx }) => {
358
+
359
+
360
+
361
+ // 関数の中は変更していないため省略
362
+
363
+
364
+
365
+ });
366
+
367
+
368
+
369
+ ```

1

key={v4()}をkey={idx}に変更

2021/07/06 12:45

投稿

tksx1227
tksx1227

スコア6

test CHANGED
File without changes
test CHANGED
@@ -26,10 +26,6 @@
26
26
 
27
27
 
28
28
 
29
- 再レンダリングが生じることで、inputに文字を1文字でも入力した途端にフォーカスが外れるため、連続で文字を入力することができません。
30
-
31
-
32
-
33
29
  この問題の対処法が全く分からず、こちらで質問するに至りました。
34
30
 
35
31
  どなたか知恵を貸していただけるとありがたいです。
@@ -49,8 +45,6 @@
49
45
  import { useState, useCallback } from "react";
50
46
 
51
47
  import { Child } from "./Child";
52
-
53
- import { v4 } from "uuid";
54
48
 
55
49
 
56
50
 
@@ -108,7 +102,7 @@
108
102
 
109
103
  // listの最後に要素を追加したとしても、それ以前の要素も全て再描画の対象になる
110
104
 
111
- <Child key={v4()} item={item} setItem={(item) => setItem(idx, item)} />
105
+ <Child key={idx} item={item} setItem={(item) => setItem(idx, item)} />
112
106
 
113
107
  );
114
108