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

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

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

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

Q&A

解決済

1回答

1445閲覧

Reactで変更したstateがすぐに反映されずに困っています。

k10a

総合スコア35

React.js

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

2グッド

1クリップ

投稿2018/07/16 08:04

編集2018/07/17 03:00

前提・実現したいこと

Reactで変更したstateが画面上ですぐに反映されずに困っています。Itemの数量を子コンポーネントのクリックに応じて増やすという仕様で、console.logで見てみると、問題なく増えているのですが、画面でrenderしている箇所には反映されていません。

試してみたこと

ReactではsetStateは即座に反映されない

上記の記事を参考に、変数を加えてみたのですが現状は変わっていません。
また、setStateで書き換えて見ましたがこちらもうまくいきませんでした。

該当のソースコード

◾️Select.js

import React, { Component } from 'react'; import ItemList from './common/ItemList'; class Select extends Component { constructor(props) { super(props); this.state = { items: [ { id: 1, name: 'apple', chosen: false, quantity: 0 }, { id: 2, name: 'banana', chosen: false, quantity: 0 } ] }; } _handleAdd = id => event => { this.setState((prevState, props) => ({ items: prevState.items.map(prevItem => { if(prevItem.id !== id) return prevItem; const quantity = prevItem.quantity + 1; return { quantity, ...prevItem }; }) })); console.log('state: ', this.state); } render() { return ( <div className="App"> <h1>SelectItem</h1> <ItemList items={this.state.items} handleAdd={this._handleAdd} /> </div> ); } } export default Select;

◾️SelectList.js

JavaScript

1import React from 'react'; 2import SelectedItem from './common/SelectedItem'; 3 4const SelectedItemList = ({ items, handleAdd }) => ( 5 items.map(item => ( 6 <SelectedItem 7 key={item.id} 8 item={item} 9 handleAdd={handleAdd} 10 /> 11 )) 12); 13 14export default SelectedItemList

◾️SelectedItem.js

JavaScript

1import React from 'react'; 2 3const SelectedItem = ({ item, handleAdd }) => ( 4 <button onClick={handleAdd(item.id) /> 5 <p>{item.quantity}個購入します</p> 6); 7 8export default SelectedItem
sabx👍を押しています

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

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

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

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

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

og24715

2018/07/17 05:52

state の確認は render メソッド内で行ったほうが確実です
k10a

2018/07/17 06:15

貴重なご指摘ありがとうございます。
guest

回答1

0

ベストアンサー

Reactの場合、state内の値はthis.setStateを使って書き換えないと、Reactが変更を認識できません。

そして、前のstateを見て次のstateを作るような場合、それ用のコールバックが必要となります(React公式)。

javascript

1 _handleAdd = id => event => { 2 this.setState((prevState, props) => ({ 3 items: prevState.items.map(prevItem => { 4 if(prevItem.id !== id) return prevItem; 5 const percent = prevItem.quantity + 1; 6 return { percent, ...prevItem }; 7 }) 8 })); 9 }

投稿2018/07/16 10:01

編集2018/07/16 10:03
maisumakun

総合スコア145183

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

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

k10a

2018/07/17 01:06

ご回答頂きまして誠にありがとうございます。頂いた資料と、ソースコードを参考に更新してみたのですが、console上も変化なしとなってしまいました。 bindなどする必要があるのでしょうか。
Meganezaru

2018/07/17 01:34

実際のstateは、非同期で変更されることになるので、 _handleAddのconsole.logを処理するタイミングでは、変更が反映されないですね。 実際のコントロールも、表示されないのでしょうか?
k10a

2018/07/17 01:58

ご回答ありがとうございます。 <p>{item.cart}個購入します</p> の部分も0のままとなっています。 子コンポーネントを参照している点も何か関係しているのでしょうか。
Meganezaru

2018/07/17 02:12

質問のソースは、maisumakunさんの回答にしたがって修正した内容ですか? 今の内容だと、item.quantityに個数が入るように見えます。 prevItem.cartを更新しないといけないように思いますが、どうでしょう?
k10a

2018/07/17 03:01

大変申し訳ございませんでした。こちら、cartとquantityが分けて記されていました。 quantityで統一して、ソースコードを書き換えて見たのですが、やはり動きませんでした。
Meganezaru

2018/07/17 05:05

Select.jsでimportしているItemListとSelectListは別物ですよね? そこのつながりが影響しているような・・・
k10a

2018/07/17 05:11

ありがとうございます。一度、ソースコードを見直して見ます。
k10a

2018/07/17 06:15

正しいやり方かは分かりませんが、下記で正常に動きました。 ``` item.quantity = item.quantity - 5; this.setState({ item }); ```
og24715

2018/07/17 06:17

@maisumakun さん > そして、前のstateを見て次のstateを作るような場合、それ用のコールバックが必要となります ちょっと語弊を感じました。 setState は updater と callback の2つを引数に取りますが、恐らく maisumakun さんの差す コールバックとは updater です。 更には、前の state を元に新しい state を作り出すなら updater は関数である必要はありません。 this.setState({ count: this.state.count + 1 }) とすることでカウンターをインクリメント出来ます。 updater が関数(?コールバック)である必要は、以前の state を保証するためです。 setState が非同期であるが故に、複数の setState の呼び出しによりキューイングが発生した時、 state が期待通りの値でない可能性があるため、以前の state を保証するために updater に関数を使用し、prevState 引数として以前の state を取ります。
Meganezaru

2018/07/17 08:36

@og24715 さん 最終的には、最後のsetStateで渡されたstateになるはずなので、その呼出が行われた際のstateが保証されないと、正しい値にならない可能性があると思いますが、どうでしょう? もしそうであれば、maisumakunさんの回答が、正しいように思います。
og24715

2018/07/17 09:11

@Meganezaru さん > どうでしょう? その解釈で間違いないありません。 maisumakun さんの回答が間違いとも言ってません。 "以前の state に依存した state の更新" は必ずしも updater がコールバック関数である必要はないという指摘です。
Meganezaru

2018/07/18 00:27

@og24715 さん this.setState({ count: this.state.count + 1 }) が正しく動作するかどうかは、状況依存ですよね? this.state.countに必ず最新状態が保存されていると確信できる時。 setStateが呼び出される間隔が十分に長い、 という条件(あやふやで申し訳ないですw)かな? そういう意味合いでいけば、 直前ステータスを利用してステータスを更新する場合は、 コールバックを使いましょう!の方が安心、ってことだと思います。 (maisumakunさん、違ってたらごめんなさいw) コールバックを利用することに、弊害があるのであれば、 それとトレードオフする必要はあると思いますが、 特にない気もします。学習コスト?とかですかね? ちょっと、考えてみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問