質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.50%
Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

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

Q&A

0回答

563閲覧

react-redux ネスト下位のcontainerが持つmapStateToPropsが更新されない

tiiseto

総合スコア0

Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

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

0グッド

0クリップ

投稿2020/07/05 15:08

編集2022/01/12 10:55

実現したいこと、知りたいこと

react-redux, react-beautiful-dndを利用してtrelloクローンを作成している初心者です。

現在、以下のようなネスト構成で開発し始めています

App.js └─ MainContent.js ├─ Lists.js // container │ └─ List.js // presentation │ └ Cards.js // container │ └ Card.js // presentation │ │ └─ AddLists.js // container

containerはreact-reduxのconnect()で接続されたコンポーネントという意味で話します。

例えば、
store.stateが更新されて、
Lists.js、Cards.jsがconnect()で接続されており、それらのmapStateToProps()がオブジェクトを返すとします。
<br>
その時、
両者のmapStateToProps()がまったく同じオブジェクト内容を返すとしても、
ネストの上位のファイルのmapStateToProps()しか実行されず、
同じ内容を取得するはずの下位のファイルのmapStateToProps()は実行されないので
下位のファイルでは更新されたstore.stateが取得できずに
困っています。

私のreact-reduxへの理解は、store.stateが更新されれば必ずすべてのmapStateToProps()が実行するものと思っていました。
しかしどうもこの理解を修正しなくてはいけないようです。

そこで知りたいのが、
「react-reduxは、connect()するコンポーネントをネストの一番上だけに限定して、下位の途中に追加してはいけない仕様なのでしょうか?」ということです。

すこしネットで探してみると、ネストの途中でconnect()しているコンポーネントを挟んでいるようなコードは見かけます。
react-reduxの公式を見てみても、
store.stateが更新されたらすべての接続されたmapStateToProps()は実行されるとあります。

この公式の文言は、別々のネスト同士の場合という前提で理解すべきでしょうか?
それとも別のところに問題があるのでしょうか?
やり方の問題ではあるのでしょうが、どこを修正すればいいのか見当がつきません。

どうかこのことについて詳しい方は、教えてくださいませんでしょうか。

エラー内容など

エラー内容:

TypeError: Cannot read property 'content' of undefined

エラー発生個所 : Cards.js

エラー発生理由 : Cards.jsのmapStateToProps()でstate.cardsが更新されず空オブジェクトのままで、その中身を読み取ろうとしたから。

const mapStateToProps = state => { return { cards: state.cards, // 更新されなかった } }

ソースコード

  • store.stateのひな型になるオブジェクト

JavaScript

1const initialData = { 2 3 // StoreCards 4 cards: { 5 6 // 'card-1': { id: 'card-1', content: 'Take out the garbage' }, 7 // 'card-2': { id: 'card-2', content: 'Wtatch my favorite show' }, 8 // 'card-3': { id: 'card-3', content: 'Change my phone' }, 9 // 'card-4': { id: 'card-4', content: 'Cook dinner' }, 10 }, 11 lists: { 12 13 // 'list-1': { 14 // id: 'list-1', 15 // title: 'add a list...', 16 // cardIds: ['card-1', 'card-2', 'card-3'], 17 // }, 18 19 // 'list-2': { 20 // id: 'list-2', 21 // title: 'add a list...2', 22 // cardIds: ['card-4'], 23 // }, 24 25 // 'list-3': { 26 // id: 'list-3', 27 // title: 'add a list...3', 28 // cardIds: ['card-5', 'card-6'], 29 // }, 30 }, 31 32 listOrder: [ 33 // 'list-1', 'list-2', 'list-3' 34 ], 35} 36 37export default initialData; 38
  • reducer/index.js
    CREATE_NEW_CARDでstateを更新したのちの話です。

JaVaScript

1// root reducer 2 3import * as actionTypes from '../../action_namespace'; 4import initialData from '../../initialData'; 5 6const initialState = { ...initialData }; 7 8const reducer = (state = initialState, action) => { 9 10 switch (action.type) { 11 case actionTypes.CREATE_NEW_CARD: 12 13 console.log('create new card'); 14 15 if (state.listOrder.includes(action.list)) { 16 17 const newCards = { ...state.cards }; 18 const amountOfCards = Object.keys(newCards).length; 19 newCards[`card-${amountOfCards + 1}`] = { 20 id: `card-${amountOfCards + 1}`, 21 content: action.content, 22 }; 23 24 const newLists = { ...state.lists }; 25 newLists[action.list].cardIds.push(`card-${amountOfCards + 1}`); 26 27 return { 28 ...state, 29 cards: newCards, 30 lists: newLists, 31 }; 32 33 } 34 else { 35 return state; 36 } 37 38 default: return state; 39 } 40} 41 42export default reducer;
  • App.js
import React from 'react'; import { DragDropContext } from 'react-beautiful-dnd'; import { connect } from 'react-redux'; import MainContent from './MainContent'; import classes from './App.module.css'; import { cardMoveToSameList } from '../store/action'; import * as actionTypes from '../action_namespace'; class App extends React.Component { onDragEnd = result => {} render() { return ( <DragDropContext onDragEnd={this.onDragEnd}> <div className={classes.App}> <MainContent /> </div> </DragDropContext> ); } } export default App;
  • Lists.js
import React from 'react'; import { connect } from 'react-redux'; import classes from './Lists.module.css'; import List from './List'; class Lists extends React.Component { render() { console.log(this.props); let lists = this.props.listOrder.map(list => { return ( <List className={classes.Lists} key={this.props.lists[list].id} id={this.props.lists[list].id} title={this.props.lists[list].title} listName={list} cards={this.props.cards} /> ); }); return ( <div className={classes.Lists} > {lists} </div> ); } } // 更新される const mapStateToProps = state => { return { cards: state.cards, lists: state.lists, listOrder: state.listOrder } } export default connect(mapStateToProps)(Lists);
  • List.js
import React from 'react'; import Cards from './Cards'; import classes from './List.module.css'; const list = props => { return ( <div id={props.id} className={classes.List} > <span>{props.title}</span> <Cards listName={props.listName} cards={props.cards} /> </div> ); } export default list;
  • Cards.js
import React from 'react'; import { connect } from 'react-redux'; import { Droppable } from 'react-beautiful-dnd'; import DroppableContainer from '../dnd/droppableContainer/droppableContainer'; import classes from './Cards.module.css'; import Card from './Card'; import { createNewCard } from '../store/action'; // @props : List.jsからprops.listNameを引き継ぐ class Cards extends React.Component { state = { inputValue: '' } keyUpHandler = (event) => { const ENTER_KEY = 13; if (event.keyCode === ENTER_KEY && this.state.inputValue !== '') { this.props.onCreateNewCard(this.props.listName, this.state.inputValue); this.setState({ inputValue: '' }); } } addCardHandler = () => { if (this.state.inputValue !== '') { this.props.onCreateNewCard(this.props.listName, this.state.inputValue); this.setState({ inputValue: '' }); } } onChangeHandler = (event) => { this.setState({ inputValue: event.target.value }); } render() { // this.props.lists[].cardIdsには要素があるのに、 // this.props.cardsが空オブジェクトなので、 // エラーが起こる const cardsComp = this.props.lists[this.props.listName].cardIds.map((cardId, index) => { return ( <Card key={cardId} id={cardId} content={this.props.cards[cardId].content} // エラー個所 index={index} /> ) }); return ( <div className={classes.Cards}> <Droppable droppableId={this.props.listName}> {(provided, snapshot) => ( <DroppableContainer innerRef={provided.innerRef} {...provided.droppableProps} provided={provided} > {cardsComp} {provided.placeholder} </DroppableContainer> )} </Droppable> </div> ); } } // store.stateが更新されたのに更新されない const mapStateToProps = state => { return { cards: state.cards, // state.cardsが更新されない lists: state.lists, listOrder: state.listOrder, } } const mapDispatchToProps = dispatch => { return { onCreateNewCard: (list, card) => dispatch(createNewCard(list, card)), } } export default connect(mapStateToProps, mapDispatchToProps)(Cards);

やってみたこと

  • ネストの上位のmapStateToProp()しか実行されないのはLists.jsやCards.jsに限定された事象なのか?

    App.jsをconnect()してmapStateToProps()を定義したら、App.jsのmapStateToProps()が実行されて、Lists.jsのほうは実行されなかったので、限定された事象ではなかった。

  • store.stateは正しく更新されているのか?

    loggerを定義して更新前後のstateをコンソールに出力させましたが、正しく更新されているのは確認できました。

  • 「mapStateToProps()が実行されない」の定義は?

    mapStateToProps()内にconsole.log()を設けました。コンソールに出力されなかった、且つ実際にmapStateToProps()が更新されたstore.stateを受け取っていなかったとき実行されなかったと判断しています。

補足情報

package.json

{ "dependencies": { "react": "^16.13.1", "react-beautiful-dnd": "^13.0.0", "react-dom": "^16.13.1", "react-redux": "^7.2.0", "react-scripts": "3.4.1", "redux": "^4.0.5" },

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2020/07/12 02:33 編集

react-beautiful-dnd は内部で redux を使ってるので、それと衝突してるのかも。解決方法は分かりません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問