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

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

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

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

React.js

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

Q&A

解決済

1回答

6436閲覧

React で親コンポーネントから子コンポーネントの state を変更する方法

Yhaya

総合スコア439

JavaScript

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

React.js

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

0グッド

0クリップ

投稿2019/12/07 04:22

編集2019/12/07 05:33

実現したいこと

現在、以下のようなJavaScriptコードをReactを使って書いています。下に示しているのは2つのReactコンポーネントSideBarPaperFolderで、SideBarの子要素がPaperFolderという関係にあります。PaperFolderはMaterial-UIの<List>を使って書いており、それをSideBarが持つstateのfolderという配列に入れて表示します。

今、私が実現したいと思っている機能は、PaperFolder要素がクリック(選択)されたときにその要素の背景色を変え、以前に選択されていた要素がある場合には背景色をもとに戻すという機能です。何番目の要素が選択されているかは親要素のselectedIdxで持ちます。このstateはhandleSelectedIdxというメソッドを子要素(PaperFolder)内のselectメソッドから呼び出すことによって変更します。ここまでは自力で実装することができたのですが、ある要素をクリックしたときにすでに選択状態になっている要素があったときに選択状態にある要素の背景色をもとに戻すという機能が実装できません。親要素で現在選択している要素のインデックスをもっているので(selectedIdx)、配列folderselectedIdx番目のPaperFolder要素のselected要素を変更すれば良いのだろうということはわかるのですが、これはどのようにしたら書くことができますでしょうか?

javascript

1class SideBar extends React.Component { 2 constructor(props) { 3 super(props); 4 5 this.state = { 6 isOpen: true, 7 numFolders: 0, 8 folders: [], 9 selectedIdx: null, 10 }; 11 12 this.handleDrawer = this.handleDrawer.bind(this); 13 this.createFolder = this.createFolder.bind(this); 14 } 15 16 handleDrawer() { 17 this.setState({isOpen: !this.state.isOpen}); 18 } 19 20 handleSelectedIdx(message) { 21 this.setState({selectedIdx: message.selectedIdx}); 22 } 23 24 createFolder() { 25 this.setState({ 26 folders: this.state.folders.concat( 27 <PaperFolder 28 key={this.state.numFolders} 29 index={this.state.numFolders} 30 deleteFunc={this.deleteFolder.bind(this)} 31 callback={this.handleSelectedIdx} 32 /> 33 ) 34 }); 35 36 this.setState({ numFolders: this.state.numFolders + 1 }); 37 } 38 39 render() { 40 const { classes } = this.props; 41 42 return ( 43 <Drawer 44 variant="permanent" 45 open={this.state.isOpen} 46 > 47 <div className={classes.allFolders}> 48 { this.state.folders } 49 </div> 50 </Drawer> 51 ) 52 } 53}

javascript

1class PaperFolder extends React.Component { 2 constructor(props) { 3 super(props); 4 5 this.state = { 6 selected: false, 7 } 8 9 this.handleListClick = this.handleListClick(this); 10 this.select = this.select.bind(this); 11 } 12 13 handleListClick() { 14 this.setState({selected: !this.state.selected}); 15 } 16 17 select() { 18 this.setState({selected: !this.state.selected}); 19 this.props.callback({'selectedIdx': this.props.index}); 20 } 21 22 render() { 23 const { classes } = this.props; 24 const folderStyle = clsx({ 25 [classes.folder]: true, 26 [classes.selected]: this.state.selected 27 }); 28 29 // TODO: move <List> out of PaperFolder component 30 return ( 31 <List className={classes.folder} onClick={this.select}> 32 <ListItem 33 className={folderStyle} 34 onClick={this.handleListClick} 35 > 36 </ListItem> 37 </List> 38 ) 39 } 40}

追記

親要素からpropsとして渡して制御するように変更してみました。親要素に新たに各PaperFolder要素が選択されているかされていないかを知るための配列(folderSelectedList)を導入してこれをpropsとして子要素に渡すようにしてみたのですがうまく動作しません。どこがおかしいのでしょう?

javascript

1class SideBar extends React.Component { 2 constructor(props) { 3 super(props); 4 5 this.state = { 6 isOpen: true, 7 numFolders: 0, 8 folders: [], 9 folderSelectedList: [], 10 selectedIdx: null, // store the index of folder selected or null 11 }; 12 13 this.handleDrawer = this.handleDrawer.bind(this); 14 this.createFolder = this.createFolder.bind(this); 15 this.handleSelectedIdx = this.handleSelectedIdx.bind(this); 16 } 17 handleSelectedIdx(message) { 18 var folderSelectedList = this.state.folderSelectedList.slice(); 19 if (this.state.selectedIdx !== null) { 20 folderSelectedList[this.state.selectedIdx] = false; 21 } 22 this.setState({selectedIdx: message.selectedIdx}); 23 folderSelectedList[this.state.selectedIdx] = true; 24 this.setState({folderSelectedList: folderSelectedList}); 25 } 26 27 createFolder() { 28 this.setState({ 29 folderSelectedList: this.state.folderSelectedList.concat(false), 30 folders: this.state.folders.concat( 31 <PaperFolder 32 key={this.state.numFolders} 33 index={this.state.numFolders} 34 isSelected={this.state.folderSelectedList[this.state.numFolders]} 35 deleteFunc={this.deleteFolder.bind(this)} 36 callback={this.handleSelectedIdx} 37 /> 38 ) 39 }); 40 41 this.setState({ numFolders: this.state.numFolders + 1 }); 42 } 43 // ... 44}

javascript

1class PaperFolder extends React.Component { 2 constructor(props) { 3 super(props); 4 5 this.state = { 6 name: "Untitled", 7 } 8 9 this.handleClose = this.handleClose.bind(this); 10 this.select = this.select.bind(this); 11 } 12 13 select() { 14 // this.setState({selected: !this.state.selected}); 15 this.props.callback({'selectedIdx': this.props.index}) 16 } 17 18 render() { 19 const { classes } = this.props; 20 const folderStyle = clsx({ 21 [classes.folder]: true, 22 [classes.selected]: this.props.isSelected 23 }); 24 25 // TODO: move <List> out of PaperFolder component 26 return ( 27 <List className={classes.folder} onClick={this.select}> 28 <ListItem 29 className={folderStyle} 30 onClick={this.handleListClick} 31 > 32 </ListItem> 33 </List> 34 ) 35 } 36}

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

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

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

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

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

guest

回答1

0

ベストアンサー

これはどのようにしたら書くことができますでしょうか?

そのような書き方はすべきでありません。外部から制御しなければならない状態に関しては、コンポーネントにstateとして持たせるのではなく、外部からpropsとして供給する、というのがReactで書くときの原則です。

投稿2019/12/07 04:24

maisumakun

総合スコア145183

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

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

Yhaya

2019/12/07 05:34

書き直してみたのですが、うまく動きません。質問に追記したので見ていただけないでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問