🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
JavaScript

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

React.js

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

Q&A

解決済

1回答

4381閲覧

React で親のstateの更新で子のpropsの更新がされない

Yhaya

総合スコア439

JavaScript

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

React.js

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

0グッド

0クリップ

投稿2019/12/08 07:57

編集2019/12/08 12:19

質問したいこと

以下のような親(SideBar)と子(PaperFolder)の関係にあるコンポーネントをReactで書いています。

javascript

1class SideBar extends React.Component { 2 constructor(props) { 3 super(props); 4 5 this.state = { 6 folders: [], 7 isSelected: false, 8 }; 9 10 this.createFolder = this.createFolder.bind(this); 11 this.changeBackColor = this.changeBackColor.bind(this); 12 } 13 14 changeBackColor(message) { 15 this.setState({isSelected: !this.state.isSelected}); 16 } 17 18 createFolder() { 19 this.setState({ 20 folders: this.state.folders.concat( 21 <PaperFolder 22 key={this.state.numFolders} 23 isSelected={this.state.isSelected} 24 changeBackColor={this.changeBackColor} 25 /> 26 ) 27 }); 28 } 29 30 render() { 31 return ( 32 // ... 33 ) 34 } 35}

javascript

1class PaperFolder extends React.Component { 2 constructor(props) { 3 super(props); 4 5 this.state = { 6 name: "Untitled", 7 selected: false, 8 } 9 10 this.select = this.select.bind(this); 11 } 12 13 select() { 14 this.props.changeBackColor(); 15 } 16 17 render() { 18 const { classes } = this.props; 19 const filenameText = <ListItemText primary={this.state.name} onDoubleClick={this.openTextbox}/>; 20 const inputText = <TextField 21 value={this.state.value} 22 onChange={this.handleValue} 23 onKeyDown={this.rename} 24 autoFocus 25 />; 26 const folderStyle = clsx({ 27 [classes.folder]: true, 28 [classes.selected]: this.props.isSelected, 29 }); 30 31 return ( 32 <List className={classes.folder} onClick={this.select}> 33 <ListItem 34 className={folderStyle} 35 onClick={this.handleListClick} 36 > 37 // ... 38 </ListItem> 39 </List> 40 ) 41 } 42} 43 44const styles = theme => ({ 45 folder: { 46 paddingBottom: 0, 47 paddingTop: 5, 48 }, 49 50 selected: { 51 backgroundColor: "#e6e6e6", 52 } 53}); 54 55 56export default withStyles(styles)(PaperFolder); 57

親のstateであるisSelectedを子のpropsとして渡しています。ここで、子要素がクリックしたときに子のselected()というメソッドが呼び出されて、その中では親コンポーネントのchangeBackColor()を呼び出しています。

親のchangeBackColorではisSelectedの反転を行っています。

私は親と子のisSelectedが関連付けられているのでこの処理によって子のisSelectedも更新されるだろうと考えていたのですが、結果は(親の)this.state.isSelectedは更新されるのに(子の)this.props.isSelectedは更新されないという挙動になってしまいました。

子のthis.props.isSelectedに親の変更を反映させるためにはどうすれば良いのでしょうか?初歩的な質問ですがよろしくおねがいします。


追記

maisumakunさんの指摘を受けてList要素を直接配列に入れるのではなく、要素の持つ情報のみを配列に入れ、List要素自体はmapを使って生成するように変更しましたが、未だに親要素のisSeletectedは更新されるのに小要素のものは更新されないという現象が続いています。

javascript

1class SideBar extends React.Component { 2 constructor(props) { 3 super(props); 4 5 this.state = { 6 folders: [], 7 isSelected: false, 8 }; 9 10 this.createFolder = this.createFolder.bind(this); 11 this.changeBackColor = this.changeBackColor.bind(this); 12 } 13 14 changeBackColor(message) { 15 this.setState({isSelected: !this.state.isSelected}); 16 console.log(this.state.isSelected); 17 } 18 19 // このメソッドを書き換えた 20 createFolder() { 21 this.setState({ 22 folders: this.state.folders.concat( 23 { 24 key: this.state.numFolders, 25 isSelected: this.state.isSelected, 26 deleteFunc: this.deleteFolder, 27 changeBackColor: this.changeBackColor 28 } 29 ) 30 }); 31 } 32 33 34 render() { 35 const { classes } = this.props; 36 const folders = this.state.folders; 37 38 return ( 39 <Drawer 40 variant="permanent" 41 classes={{ 42 paper: sidebarStatus // hayashi: what is the paper? 43 }} 44 open={this.state.isOpen} 45 > 46 <div className={classes.allFolders}> 47 // map を使って子を生成するようにした 48 { folders.map((folder) => 49 <PaperFolder 50 key={folder.key} 51 isSelected={folder.isSelected} 52 deleteFunc={folder.deleteFunc} 53 changeBackColor={folder.changeBackColor} 54 /> 55 )} 56 </div> 57 </Drawer> 58 ) 59 } 60} 61

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

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

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

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

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

maisumakun

2019/12/08 08:19

子のほうのstateにもselectedがありますが、間違ってそちらを参照してしまっている、ということはないでしょうか。
Yhaya

2019/12/08 08:24

クリックしたときに親の方と子の方でそれぞれ`alert(this.state.isSelected)`と`alert(this.props.isSelected)`を(changeBackColorメソッド内に挿入して)確認しているのですが値が異なってしまっています。
maisumakun

2019/12/08 08:26

(子の方に、実際にどのような形でpropsを参照したかがないので、なんともいえない感じです)
Yhaya

2019/12/08 08:33

失礼しました。コードを追記しました。 render()のなかでスタイル(folderStyle)を定義していて、その中でisSelectedを使っています。このスタイルは<ListItem>の中でclassNameを決めるのに使っています。
guest

回答1

0

ベストアンサー

子コンポーネントをcreateFolderで配列に登録していますが、このようなやり方をすると子コンポーネントのprops配列に追加した時点のもので固定されてしまいます。

あえてこのような動作にしたい場合は別ですが、そうでない場合は配列には素材だけ入れておいて、render()(あるいはそこから呼び出される関数)内で配列をmapして子コンポーネントを生成してください。

投稿2019/12/08 08:37

maisumakun

総合スコア145963

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

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

Yhaya

2019/12/08 08:47

配列に登録して使うとその時点で固定されてしまうというのは初耳でした。mapを試してみます。
Yhaya

2019/12/08 12:20

ご指摘を受けて書き直してみたのですが、問題の現象が続いております。質問本文にコードを追記したので見ていただけますか?
maisumakun

2019/12/08 12:38

isSelectedも配列に保存しているので、当然ながらそれは保存時の値で、後のstate.isSelectedの変更は追従しません。 render時に直接state.isSelectedを参照するようにしてください。
Yhaya

2019/12/08 12:51

なるほど、isSelectedも配列の外に出さなければならないんですね。ありがとうございます、解決しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問