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

質問編集履歴

2

余計な文言削除

2020/07/08 12:02

投稿

tiiseto
tiiseto

スコア0

title CHANGED
File without changes
body CHANGED
@@ -15,7 +15,7 @@
15
15
  └─ AddLists.js // container
16
16
  ```
17
17
  `container`はreact-reduxの`connect()`で接続されたコンポーネントという意味で話します。
18
- <br>
18
+
19
19
  例えば、
20
20
  `store.state`が更新されて、
21
21
  Lists.js、Cards.jsが`connect()`で接続されており、それらの`mapStateToProps()`がオブジェクトを返すとします。
@@ -26,24 +26,24 @@
26
26
  同じ内容を取得するはずの下位のファイルの`mapStateToProps()`は実行されないので
27
27
  下位のファイルでは更新された`store.state`が取得できずに
28
28
  困っています。
29
- <br>
29
+
30
30
  私のreact-reduxへの理解は、`store.state`が更新されれば必ずすべての`mapStateToProps()`が実行するものと思っていました。
31
31
  しかしどうもこの理解を修正しなくてはいけないようです。
32
- <br>
32
+
33
33
  そこで知りたいのが、
34
34
  「react-reduxは、`connect()`するコンポーネントをネストの一番上だけに限定して、下位の途中に追加してはいけない仕様なのでしょうか?」ということです。
35
- <br>
35
+
36
36
  すこしネットで探してみると、ネストの途中で`connect()`しているコンポーネントを挟んでいるようなコードは見かけます。
37
37
  react-reduxの公式を見てみても、
38
38
  `store.state`が更新されたらすべての接続された`mapStateToProps()`は実行されるとあります。
39
- <br>
39
+
40
40
  この公式の文言は、別々のネスト同士の場合という前提で理解すべきでしょうか?
41
41
  それとも別のところに問題があるのでしょうか?
42
42
  やり方の問題ではあるのでしょうが、どこを修正すればいいのか見当がつきません。
43
- <br>
43
+
44
44
  どうかこのことについて詳しい方は、教えてくださいませんでしょうか。
45
- <br>
46
45
 
46
+
47
47
  ### エラー内容など
48
48
 
49
49
  エラー内容:
@@ -51,7 +51,7 @@
51
51
  TypeError: Cannot read property 'content' of undefined
52
52
  ```
53
53
  エラー発生個所 : Cards.js
54
- <br>
54
+
55
55
  エラー発生理由 : Cards.jsのmapStateToProps()でstate.cardsが更新されず空オブジェクトのままで、その中身を読み取ろうとしたから。
56
56
 
57
57
  ```
@@ -344,18 +344,18 @@
344
344
  ### やってみたこと
345
345
 
346
346
  - ネストの上位のmapStateToProp()しか実行されないのはLists.jsやCards.jsに限定された事象なのか?
347
- <br>
347
+
348
348
  App.jsをconnect()してmapStateToProps()を定義したら、App.jsのmapStateToProps()が実行されて、Lists.jsのほうは実行されなかったので、限定された事象ではなかった。
349
349
 
350
350
  - store.stateは正しく更新されているのか?
351
- <br>
351
+
352
352
  loggerを定義して更新前後のstateをコンソールに出力させましたが、正しく更新されているのは確認できました。
353
353
 
354
354
  - 「mapStateToProps()が実行されない」の定義は?
355
- <br>
355
+
356
356
  mapStateToProps()内にconsole.log()を設けました。コンソールに出力されなかった、且つ実際にmapStateToProps()が更新されたstore.stateを受け取っていなかったとき実行されなかったと判断しています。
357
- <br>
358
357
 
358
+
359
359
  ### 補足情報
360
360
 
361
361
  package.json

1

同一ネストに複数コンテナがあるかどうかが問題だと認識したので質問内容を変更しました。

2020/07/08 12:02

投稿

tiiseto
tiiseto

スコア0

title CHANGED
@@ -1,1 +1,1 @@
1
- react-redux mapStateToProps()が親コンポーでは更新されるけど、孫コンポーネントでは更新されない
1
+ react-redux 下位のcontainerが持つmapStateToPropsが更新されない
body CHANGED
@@ -1,110 +1,77 @@
1
- ### 前提・実現したいこと
1
+ ### 実現したいこと、知りたいこと
2
2
 
3
+ react-redux, react-beautiful-dndを利用してtrelloクローンを作成している初心者です。
3
4
 
5
+ 現在、以下のようなネスト構成で開発し始めています
6
+ ```
7
+ App.js
8
+ └─ MainContent.js
9
+ ├─ Lists.js // container
10
+ │ └─ List.js // presentation
11
+ │ └ Cards.js // container
12
+ │ └ Card.js // presentation
13
+
14
+
15
+ └─ AddLists.js // container
16
+ ```
17
+ `container`はreact-reduxの`connect()`で接続されたコンポーネントという意味で話します。
18
+ <br>
19
+ 例えば、
20
+ `store.state`が更新されて、
21
+ Lists.js、Cards.jsが`connect()`で接続されており、それらの`mapStateToProps()`がオブジェクトを返すとします。
22
+ <br>
23
+ その時、
24
+ 両者の`mapStateToProps()`がまったく同じオブジェクト内容を返すとしても、
25
+ ネストの上位のファイルの`mapStateToProps()`しか実行されず、
26
+ 同じ内容を取得するはずの下位のファイルの`mapStateToProps()`は実行されないので
27
+ 下位のファイルでは更新された`store.state`が取得できずに
4
- 初めまして。
28
+ 困っいます
29
+ <br>
30
+ 私のreact-reduxへの理解は、`store.state`が更新されれば必ずすべての`mapStateToProps()`が実行するものと思っていました。
31
+ しかしどうもこの理解を修正しなくてはいけないようです。
32
+ <br>
33
+ そこで知りたいのが、
34
+ 「react-reduxは、`connect()`するコンポーネントをネストの一番上だけに限定して、下位の途中に追加してはいけない仕様なのでしょうか?」ということです。
35
+ <br>
36
+ すこしネットで探してみると、ネストの途中で`connect()`しているコンポーネントを挟んでいるようなコードは見かけます。
5
- reactreduxなど使用しtrelloクローンを作成しいる者です。
37
+ react-reduxの公式も、
38
+ `store.state`が更新されたらすべての接続された`mapStateToProps()`は実行されるとあります。
39
+ <br>
40
+ この公式の文言は、別々のネスト同士の場合という前提で理解すべきでしょうか?
41
+ それとも別のところに問題があるのでしょうか?
42
+ やり方の問題ではあるのでしょうが、どこを修正すればいいのか見当がつきません。
43
+ <br>
44
+ どうかこのことについて詳しい方は、教えてくださいませんでしょうか。
45
+ <br>
6
46
 
47
+ ### エラー内容など
7
48
 
8
- 親コンポーネントでは`mapStateToProps()`が更新されるのに、孫コンポーネントでは更新されず困っています。
9
-
10
-
11
- 親コンポネント、子コンポーネント、孫コンポーネントとある中で
49
+ エラ内容:
12
- 親コンポーネントと孫コンポーネントが`mapStateToProps()`を使用しています。
13
-
14
-
15
- 各コンポーネントの`mapStateToProps()`が返すオブジェクトは下記のとおりです。
16
-
17
50
  ```
18
- /* 両者同じ内容です */
19
- const mapStateToProps = state => {
51
+ TypeError: Cannot read property 'content' of undefined
20
- return {
21
- cards : state.cards,
22
- lists : state.lists,
23
- listOrder : state.listOrder
24
- };
25
- }
26
52
  ```
53
+ エラー発生個所 : Cards.js
54
+ <br>
55
+ エラー発生理由 : Cards.jsのmapStateToProps()でstate.cardsが更新されず空オブジェクトのままで、その中身を読み取ろうとしたから。
27
56
 
28
- `store.state.cards`がreducerによって更新されたときに、
29
- 親コンポーネント、孫コンポーネントの`mapStateToProps()`が実行されて
30
- 新たにオブジェクトを返すことを期待したのですが
31
-
32
-
33
-
34
- 親コンポーネントの`mapStateToProps()`は更新されて、
35
- どういうわけか、
36
- 孫コンポーネントの`mapStateToProps()`は実行されない
37
- という状況に陥っています。
38
-
39
-
40
57
  ```
41
58
  const mapStateToProps = state => {
42
59
  return {
43
- cards : state.cards, // <-- 孫の方だけ更新されない!
60
+ cards: state.cards, // 更新されなかった
44
- lists : state.lists,
45
- listOrder : state.listOrder
46
- };
47
- }
61
+ }
48
- ```
49
-
50
- 公式でも、
51
- 接続されているすべての`mapStateToProps()`は、`store.state`の更新時に実行されるとあるのですが
52
- 私の作成しているモノでは孫コンポーネントの`mapStateToProps()`は実行されませんでした。
53
-
54
-
55
- 実現したいことは
56
- 孫コンポーネントの`mapStateToProps()`が、正しく更新された`store.state`を受け取り
57
- 孫コンポーネントに`props`として更新された値が渡され
58
- それによって孫コンポーネントが再レンダリングされることです。
59
-
60
-
61
- 想定した処理順序は、
62
- reducerが`store.state.cards`のオブジェクトに新規のオブジェクトを加えて`store.state`を更新する
63
- (正しく更新されているのは確認済)
64
-
62
+ }
65
- `connect()`で接続されている親コンポーネントの`mapStateToProps()`が実行される
66
- (正しく更新されているのは確認済)
67
-
68
- `connect()`で接続されている孫コンポーネントの`mapStateToProps()`が実行される
69
- (`state.cards`が更新されない)
70
-
71
- 孫コンポーネントが`mapStateToProps()`から返された正しいオブジェクトを基に再レンダリング
72
- (`state.cards`が空なのでエラー)
73
-
74
-
75
-
76
- よろしくお願いします。
77
-
78
-
79
- ### 発生している問題・エラーメッセージ
80
-
81
- #### エラーメッセージ
82
-
83
63
  ```
84
- /* ブラウザが吐いたエラー*/
85
- TypeError: Cannot read property 'content' of undefined
86
64
 
87
- /* 発生個所 */
88
- /* src/components/Cards.js */
89
- <Card
90
- key={cardId} id={cardId}
91
- content={this.props.cards[cardId].content} // ここのcontentがundefinedになってしまう。this.props.cardsが空になっているせい
92
- index={index}
93
- />
94
65
 
95
- ```
66
+ ### ソースコード
96
67
 
97
-
98
- ### 該当ソースコード
68
+ - store.stateひな型になるオブジェクト
99
-
100
69
  ```JavaScript
101
- // store.stateの初期値として渡されるものです
102
- // コメントアウトされているような内容がreducerによって作成されます
103
- // 今回はcardsの中身が更新された後の話です
104
70
  const initialData = {
105
71
 
106
- //
72
+ // StoreCards
107
73
  cards: {
74
+
108
75
  // 'card-1': { id: 'card-1', content: 'Take out the garbage' },
109
76
  // 'card-2': { id: 'card-2', content: 'Wtatch my favorite show' },
110
77
  // 'card-3': { id: 'card-3', content: 'Change my phone' },
@@ -114,49 +81,129 @@
114
81
 
115
82
  // 'list-1': {
116
83
  // id: 'list-1',
117
- // title: 'ToDo',
84
+ // title: 'add a list...',
118
- // cardIds: ['card-1', 'card-2', 'card-3'], // 各lists[].cardsIdを基にcardsの中から
85
+ // cardIds: ['card-1', 'card-2', 'card-3'],
119
86
  // },
120
87
 
121
88
  // 'list-2': {
122
89
  // id: 'list-2',
123
- // title: 'Done',
90
+ // title: 'add a list...2',
124
91
  // cardIds: ['card-4'],
125
92
  // },
126
93
 
127
94
  // 'list-3': {
128
95
  // id: 'list-3',
129
- // title: 'Archive',
96
+ // title: 'add a list...3',
130
97
  // cardIds: ['card-5', 'card-6'],
131
98
  // },
132
99
  },
100
+
133
101
  listOrder: [
134
102
  // 'list-1', 'list-2', 'list-3'
135
103
  ],
136
104
  }
137
105
 
138
106
  export default initialData;
107
+
139
108
  ```
140
109
 
110
+ - reducer/index.js
111
+ CREATE_NEW_CARDでstateを更新したのちの話です。
112
+ ```JaVaScript
113
+ // root reducer
141
114
 
142
- ```React.js
115
+ import * as actionTypes from '../../action_namespace';
143
- /* 親コンポーネント */
116
+ import initialData from '../../initialData';
144
117
 
118
+ const initialState = { ...initialData };
119
+
120
+ const reducer = (state = initialState, action) => {
121
+
122
+ switch (action.type) {
123
+ case actionTypes.CREATE_NEW_CARD:
124
+
125
+ console.log('create new card');
126
+
127
+ if (state.listOrder.includes(action.list)) {
128
+
129
+ const newCards = { ...state.cards };
130
+ const amountOfCards = Object.keys(newCards).length;
131
+ newCards[`card-${amountOfCards + 1}`] = {
132
+ id: `card-${amountOfCards + 1}`,
133
+ content: action.content,
134
+ };
135
+
136
+ const newLists = { ...state.lists };
137
+ newLists[action.list].cardIds.push(`card-${amountOfCards + 1}`);
138
+
139
+ return {
140
+ ...state,
141
+ cards: newCards,
142
+ lists: newLists,
143
+ };
144
+
145
+ }
146
+ else {
147
+ return state;
148
+ }
149
+
150
+ default: return state;
151
+ }
152
+ }
153
+
154
+ export default reducer;
155
+ ```
156
+ - App.js
157
+ ```
145
158
  import React from 'react';
159
+ import { DragDropContext } from 'react-beautiful-dnd';
146
160
  import { connect } from 'react-redux';
161
+
162
+ import MainContent from './MainContent';
163
+ import classes from './App.module.css';
164
+ import { cardMoveToSameList } from '../store/action';
165
+ import * as actionTypes from '../action_namespace';
166
+
167
+ class App extends React.Component {
168
+
169
+ onDragEnd = result => {}
170
+
171
+ render() {
172
+ return (
173
+ <DragDropContext onDragEnd={this.onDragEnd}>
174
+ <div className={classes.App}>
175
+ <MainContent />
176
+ </div>
177
+ </DragDropContext>
178
+ );
179
+ }
180
+ }
181
+
182
+ export default App;
183
+
184
+ ```
185
+ - Lists.js
186
+ ```
187
+ import React from 'react';
188
+ import { connect } from 'react-redux';
189
+
147
190
  import classes from './Lists.module.css';
148
191
  import List from './List';
149
192
 
150
193
  class Lists extends React.Component {
151
194
 
152
195
  render() {
196
+ console.log(this.props);
197
+
153
198
  let lists = this.props.listOrder.map(list => {
154
199
  return (
155
200
  <List
201
+ className={classes.Lists}
156
202
  key={this.props.lists[list].id}
157
203
  id={this.props.lists[list].id}
158
204
  title={this.props.lists[list].title}
159
205
  listName={list}
206
+ cards={this.props.cards}
160
207
  />
161
208
  );
162
209
  });
@@ -169,6 +216,8 @@
169
216
  }
170
217
  }
171
218
 
219
+
220
+ // 更新される
172
221
  const mapStateToProps = state => {
173
222
 
174
223
  return {
@@ -180,26 +229,37 @@
180
229
 
181
230
  export default connect(mapStateToProps)(Lists);
182
231
  ```
232
+ - List.js
233
+ ```
234
+ import React from 'react';
235
+ import Cards from './Cards';
236
+ import classes from './List.module.css';
183
237
 
184
- ```React.js
185
- /*
186
- 孫コンポーネント
187
- このコンポーネントの一つ上の親コンポーネントから、
188
- listNameというpropsを受け取っています
238
+ const list = props => {
239
+ return (
240
+ <div
241
+ id={props.id} className={classes.List} >
242
+ <span>{props.title}</span>
243
+ <Cards listName={props.listName} cards={props.cards} />
189
- */
244
+ </div>
245
+ );
246
+ }
190
247
 
248
+ export default list;
249
+ ```
250
+ - Cards.js
251
+ ```
191
252
  import React from 'react';
192
253
  import { connect } from 'react-redux';
193
254
  import { Droppable } from 'react-beautiful-dnd';
194
255
 
195
256
  import DroppableContainer from '../dnd/droppableContainer/droppableContainer';
196
-
197
257
  import classes from './Cards.module.css';
198
258
  import Card from './Card';
199
- import Input from '../UI/Input';
200
- import Button from '../UI/Button';
201
259
  import { createNewCard } from '../store/action';
202
260
 
261
+
262
+ // @props : List.jsからprops.listNameを引き継ぐ
203
263
  class Cards extends React.Component {
204
264
 
205
265
  state = { inputValue: '' }
@@ -210,7 +270,6 @@
210
270
  && this.state.inputValue !== '') {
211
271
  this.props.onCreateNewCard(this.props.listName, this.state.inputValue);
212
272
  this.setState({ inputValue: '' });
213
- // focus, blurとか操作したい...
214
273
  }
215
274
  }
216
275
 
@@ -227,12 +286,16 @@
227
286
 
228
287
  render() {
229
288
 
289
+ // this.props.lists[].cardIdsには要素があるのに、
290
+ // this.props.cardsが空オブジェクトなので、
291
+ // エラーが起こる
230
- const cards = this.props.lists[this.props.listName].cardIds.map((cardId, index) => {
292
+ const cardsComp = this.props.lists[this.props.listName].cardIds.map((cardId, index) => {
293
+
231
294
  return (
232
295
  <Card
233
296
  key={cardId}
234
297
  id={cardId}
235
- content={this.props.cards[cardId].content} // <-- エラー発生個所 cardsが空であるせい
298
+ content={this.props.cards[cardId].content} // エラー個所
236
299
  index={index}
237
300
  />
238
301
  )
@@ -247,7 +310,7 @@
247
310
  {...provided.droppableProps}
248
311
  provided={provided}
249
312
  >
250
- {cards}
313
+ {cardsComp}
251
314
  {provided.placeholder}
252
315
  </DroppableContainer>
253
316
  )}
@@ -257,9 +320,12 @@
257
320
  }
258
321
  }
259
322
 
323
+
324
+ // store.stateが更新されたのに更新されない
260
325
  const mapStateToProps = state => {
326
+
261
327
  return {
262
- cards: state.cards, // <-- 更新されず空のオブジェクトのまま
328
+ cards: state.cards, // state.cardsが更新されない
263
329
  lists: state.lists,
264
330
  listOrder: state.listOrder,
265
331
  }
@@ -272,97 +338,35 @@
272
338
  }
273
339
 
274
340
  export default connect(mapStateToProps, mapDispatchToProps)(Cards);
275
-
276
- ```
277
-
278
- ```React.js
279
- /* 子コンポーネント */
280
- import React from 'react';
281
- import Cards from './Cards';
282
- import classes from './List.module.css';
283
-
284
- const list = props => {
285
- return (
286
- <div
287
- id={props.id} className={classes.List} >
288
- <span>{props.title}</span>
289
- <Cards listName={props.listName} />
290
- </div>
291
- );
292
- }
293
-
294
- export default list;
295
-
296
341
  ```
297
342
 
298
- ```JavaScript
299
343
 
300
- /* reducer */
344
+ ### やってみたこと
301
345
 
302
- import * as actionTypes from '../../action_namespace';
303
- import initialData from '../../initialData';
346
+ - ネストの上位のmapStateToProp()しか実行されないのはLists.jsやCards.jsに限定された事象なのか?
347
+ <br>
348
+ App.jsをconnect()してmapStateToProps()を定義したら、App.jsのmapStateToProps()が実行されて、Lists.jsのほうは実行されなかったので、限定された事象ではなかった。
304
349
 
305
- const initialState = { ...initialData };
306
-
307
- const reducer = (state = initialState, action) => {
308
-
309
- switch (action.type) {
310
- case actionTypes.CREATE_NEW_CARD:
311
-
312
- if (state.listOrder.includes(action.list)) {
313
-
314
- // 新しいカードを作る
315
- const newCards = { ...state.cards };
316
- // state.cardsの要素数を確認する
317
- const amountOfCards = Object.keys(newCards).length;
318
- newCards[`card-${amountOfCards + 1}`] = {
319
- id: `card-${amountOfCards + 1}`,
320
- content: action.content,
321
- };
322
-
323
-
324
- // 所属するStore.state.lists.cardIdsへ追加する
325
- const newLists = { ...state.lists };
326
- newLists[action.list].cardIds.push(`card-${amountOfCards + 1}`);
327
-
328
- return {
329
- ...state,
330
- cards: newCards,
331
- lists: newLists,
332
- };
333
-
334
- }
335
- else {
336
- return state;
337
- }
338
-
339
- default: return state;
340
- }
341
- }
342
- ```
343
-
344
-
345
- ### 試したこと
346
-
347
- - 本当にmapStateToProps()はすべて呼び出されていないのか?
348
- 親、孫のmapStateToProps()へconsole.log('map state to props');のように出力しましたが親の方しか呼び出されなかった。 (孫はコンソールに出力されなかった)
349
-
350
350
  - store.stateは正しく更新されているのか?
351
+ <br>
352
+ loggerを定義して更新前後のstateをコンソールに出力させましたが、正しく更新されているのは確認できました。
351
353
 
352
- store監視のloggerを設けた正しく更新されのは確認できました。
354
+ - 「mapStateToProps()実行され定義
355
+ <br>
356
+ mapStateToProps()内にconsole.log()を設けました。コンソールに出力されなかった、且つ実際にmapStateToProps()が更新されたstore.stateを受け取っていなかったとき実行されなかったと判断しています。
357
+ <br>
353
358
 
359
+ ### 補足情報
354
360
 
355
-
356
-
357
-
358
- ### 補足情報(FW/ツールのバージョンなど)
361
+ package.json
359
362
  ```
363
+ {
360
- package.json{
364
+ "dependencies": {
361
365
  "react": "^16.13.1",
362
366
  "react-beautiful-dnd": "^13.0.0",
363
367
  "react-dom": "^16.13.1",
364
368
  "react-redux": "^7.2.0",
365
369
  "react-scripts": "3.4.1",
366
370
  "redux": "^4.0.5"
367
- }
371
+ },
368
372
  ```