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

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

新規登録して質問してみよう
ただいま回答率
85.49%
非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

React.js

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

Q&A

0回答

1434閲覧

Reactでユーザーの選択に応じて項目をフィルタリングする、階層構造の(ドロップダウン)フォームを実装したい

Toshiyuki023

総合スコア3

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

React.js

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

0グッド

0クリップ

投稿2020/09/13 01:37

やりたいこと

ドロップダウン形式のフォームで、ユーザーが選択した項目を利用して、別のフォームの項目を制限する機能を実装したいです。
具体例として、メルカリ等で使われている様なものです。

メンズ | | - バッグ | | | - シューズ - スニーカー | |- 革靴

問題点

  1. onChangeでstateの値が変わった後に、中カテゴリが再レンダー?されない(主な問題点がこれです)
  2. setStateが非同期で処理を行っているため、初回のレンダー時にstateが書き換わっていない

コード

class Add_Give_Item_Form extends Component { constructor(props) { super(props); this.state = { // #インプット情報用 info: { name: '', owner: '', keyword1: '', keyword2: '', keyword3: '', bland: '', state: '未使用、新品', bigCategory:"" , middleCategory:"", smallCategory:"", image: '', detail: '', url: '', }, // Validation用 //  urlは必須項目ではないのでValidationには含めない message: { name: '', keyword1: '', keyword2: '', keyword3: '', bland: '', state: '', bigCategory:"" , middleCategory:"", smallCategory:"", image: '', detail: '', url: '', }, allCategory: null, }; this.handleChange = this.handleChange.bind(this); } componentDidMount() { axios .get('http://localhost:8000/api/category') .then((res) => { this.setState( { ...this.state, allCategory: res.data }) }) .catch((err) => { console.log(err); }); axios .get('http://localhost:8000/api/user/' + localStorage.getItem("uid")) .then((res) => { console.log(res) }) .catch((err) => console.log(err)); } handleChange(e) { const name = e.target.name; const value = e.target.value; const { info, message } = this.state; this.setState({ info: { ...info, [name]: value }, }); this.setState({ message: { ...message, [name]: this.validator(name, value) }, }); } // 途中省略 // render() { const { info, message, allCategory } = this.state; // setStateが完了するまではnullにする。 if(this.state.allCategory === null){return null} else { return ( <div> // 途中省略 // <label>カテゴリ</label> <select name="bigCategory" onChange={this.handleChange}> <option value="">大カテゴリ</option> { this.state.allCategory.filter((category) => category.parent === null).map((filteredCategory) => { return ( <option value={filteredCategory}>{filteredCategory.name}</option> ) }) } </select> <select name="middleCategory" onChange={this.handleChange}> <option value="">中カテゴリ</option> { this.state.allCategory.filter((category) => category.parent === this.state.info.bigCategory).map((filteredCategory) => { return (<option value="">{filteredCategory.name}</option>) }) } </select> <label>説明</label> <textarea name="detail" cols="30" rows="10" value={this.state.info.detail} onChange={this.handleChange} ></textarea> </div> ) } } } const mapStateToProps = (state) => { return { uid: state.uid, }; }; export default connect(mapStateToProps)(Add_Give_Item_Form);

詳細

onChangestateの値が変わったタイミング(ユーザーが何らかのカテゴリを選択した時)で、中カテゴリを読み込みたいのですが、再レンダーされません。

流れとしては
1.allCategoryとしてCategoryを全件取得
2.allCategoryをドロップダウンで選ばれた値に応じてfilterをする
といった形です。

カテゴリーのオブジェクトの中は、以下のようになっています(this.state.allCategoryのvalueです)。

allCategory

10: {id: 1, parent: null, name: "Men"} 21: {id: 2, parent: null, name: "Women"} 32: {id: 3, parent: "Women", name: "香水"} 43: {id: 4, parent: "Men", name: "ガジェット"} 54: {id: 5, parent: "Men", name: "スポーツ"} 65: {id: 6, parent: "Women", name: "バッグ"} 76: {id: 7, parent: "Women", name: "靴"} 87: {id: 8, parent: "靴", name: "スニーカー"} 98: {id: 9, parent: "スポーツ", name: "ユニフォーム"} 109: {id: 10, parent: "ガジェット", name: "スマートフォン"} 1110: {id: 11, parent: "ガジェット", name: "タブレット"}

大カテゴリの表示については、setStateの非同期処理を制御するために、if(this.state.allCategory === null){return null}を追加し、なんとか実装ができました。

同じ要領で、bigCategorynullの間は何も返さないように以下のコードを試してみたのですが、中カテゴリは表示されませんでした。

this.state.info.bigCategory == null ? null : this.state.allCategory.filter((category) => category.parent === this.state.info.bigCategory).map((filteredCategory) => { return (<option value="">{filteredCategory.name}</option>) })

また、bigCategory内に入るオブジェクトではなく、オブジェクトの値(今回はname)を参照してfilterをかけても、非同期通信のために、Cannot read property 'name' of nullとエラーが返ってきます。

{ this.state.info.bigCategory === null ? null : this.state.allCategory.filter((category) => category.parent.name === this.state.info.bigCategory.name).map((filteredCategory) => { return (<option value="">{filteredCategory.name}</option>) }) }

どのようにすれば、onChangestateが変わった瞬間に中カテゴリを再レンダーさせることができるのでしょうか?

非同期処理や、setStateの扱い方、レンダーなど知識不足な面が多々あるのは承知ですが、知恵をお貸しいただければ幸いです。

ご回答よろしくお願い申し上げます。

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

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

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

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

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

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問