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

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

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

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

React.js

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

Q&A

解決済

1回答

544閲覧

Reactのスプレッド構文などの理解を深めたい

KAI

総合スコア9

JavaScript

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

React.js

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

0グッド

0クリップ

投稿2023/11/29 03:21

前提

こちらの部分について、色々調べましたが、理解ができませんのでご教授いただけると幸いです。

const handleFormCheck = e => {
setForm({
...form,
[e.target.name]: e.target.checked
});
};

下記までは理解しました。
(相違しているかもしれませんが…)
処理を分解して記載します。

①「handleFormCheck」がinput属性のonChange属性により呼び出される。
②「e」にはonChange属性が動作して時の状態が保存されいる。
③setFormのステートフックスが動作しformを操作する。
以降が不明です。
...form がスプレッド構文ということはなんとなくわかるのですが、スプレッド構文とはどんな動きをするのか、調べてもイマイチ分かりません。
[e.target.name]: e.target.checked こちらに関しても、[]内は動的な〜〜と出てくるだけでよく分かりません。

詳しい方、分かりやすく解説して頂けると幸いです。

該当のソースコード

Reaxt.js

1import { useState } from 'react'; 2 3export default function FormTextarea() { 4 const [form, setForm] = useState({ 5 agreement:true 6 }); 7 8const handleFormCheck = e => { 9 setForm({ 10 ...form, 11 [e.target.name]: e.target.checked 12 }); 13}; 14 15 const show = () => { 16 console.log(`同意確認:${form.agreement ? '同意': '反対'}`); 17 }; 18 19 return ( 20 <form> 21 <label htmlFor='agreement'>同意します:</label> 22 <input id='agreement' name='agreement' type='checkbox' 23 checked={form.agreement} 24 onChange={handleFormCheck} /><br /> 25 <button type='button' onClick={show}>送信</button> 26 </form> 27 ); 28}

試したこと

グーグル検索
YouTubeの動画視聴(ほぼ出てきませんでした。)
AIに質問

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

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

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

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

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

guest

回答1

0

ベストアンサー

(1)まず、{...form}は、オブジェクトリテラルでのスプレッド構文です。(参照

オブジェクトを表す{}の中で...form と書くことで、formオブジェクトのキー(プロパティ名)と値の組が展開されます。

{}の中に、カンマで区切って、他のプロパティ名:値の組を書くことも可能です。
またスプレッド構文を書くことも可能です。

展開した結果プロパティ名が重複する場合は、マージされ、値は上書きされます。

例:

js

1let obj1 = { foo: "bar", x: 42 }; 2let obj2 = { ...obj1, y: 13 }; // obj1 のプロパティ名と値の組が展開される。 3console.log(obj2); 4>>> { foo: "bar", x: 42, y: 13 } 5 6let obj3 = {x: 50}; 7let obj4 = {...obj2, ...obj3}; // スプレッド構文を並べて書ける。それぞれのプロパティ名と値の組が展開される。 8console.log(obj4); 9>>> { foo: "bar", x: 50, y: 13 } // 展開した結果、x が重複するのでマージされ、値は50になる。

 

(2)次に、[e.target.name]: e.target.checked の部分ですが、これは前半でプロパティ名を式で表現しているものです。(参照:計算プロパティ名

たとえば、e.target.nameが「agreement」、
e.target.checked が「false」ならば、
{[e.target.name]: e.target.checked} は
{agreement : false} というオブジェクトとして評価されます。

なぜ[ ]でプロパティ名を囲むかというと、そうしないと識別子であるプロパティ名にピリオドが含まれていると解釈されて、構文エラーになるからです。
{'e.target.name': e.target.checked}と書けばエラーにはなりませんが、これだとプロパティ名が単なる「e.target.name」という文字列になってしまい、式として評価されません)

 
(3)スプレッド構文を使うと、使われた元のオブジェクトのプロパティ名が、新しいオブジェクトにコピーされます。


(1)~(3)を念頭に置いて元のコードを追ってみます。

js

1 const [form, setForm] = useState({ 2 agreement:true 3 });

ここで、form オブジェクトの初期値として、 {agreement: true}がセットされています。
同時にformのステート変更用の関数の名前として、setFormが設定されます。

jsx

1 <input id='agreement' name='agreement' type='checkbox' 2 checked={form.agreement} 3 onChange={handleFormCheck} /><br />

チェックボックスの動作・外観を定義している部分です。
checked つまりチェック状態を form オブジェクトの agreement プロパティに紐付け、チェックボックスをクリックした時のハンドラを handleFormCheck 関数に設定しています。

 

js

1const handleFormCheck = e => { 2 setForm({ 3 ...form, 4 [e.target.name]: e.target.checked 5 }); 6};

チェックボックスをクリックするとこの部分が実行されます(前述 onChange={handleFormCheck} )。

チェックボックスのname属性が「agreement」となっているので、クリックしたときに e.target.name は「agreement」と評価されます。
また、 e.target.checked はクリックされた後のチェック状態(チェックが入ればtrue、外されればfalse)になります。

仮に、チェックボックスにチェックが入っている状態でクリックしたと仮定しましょう。
このとき、このコードの部分は下記のように評価されます。

setForm({ ...form, agreement: false // チェックが外されたのでfalseになる。 })

そして、form は もともとagreement というプロパティを持っているので、

setForm({ agreement: true, agreement: false })

となり、重複しているプロパティ名 agreement がマージされて、その値は false に上書きされます。

setForm({ agreement: false })

結局、setFormには、{agreement:false} という新しいオブジェクトが渡されることになります。

この結果、useState に設定していた form という名前のオブジェクトの状態変化が React によって検知され、コンポーネントが再レンダリングされることになります。
再レンダリングの結果、 checked={form.agreement} なので、 checked=false 、つまりチェックが外れた状態で描画されます。

ここで、スプレッド構文を使わず下記のようにすればよいと思われるかもしれません。

const handleFormCheck = e => { form.agreement = e.target.checked; setForm(form); };

しかし、この場合、formオブジェクトを直接変更しているため変更が検知されず、再レンダリングされません。
参照:https://ja.react.dev/learn/updating-objects-in-state


「1つのチェックボックスを切り替えるだけなのになんでこんなめんどくさいことをやってるのだろう」と思われるかもしれません。
たしかに元のコードでは、計算プロパティ名を使わずとも、送信時にチェックボックスの状態を調べれば十分な気もします。
下記のようなコードだと計算プロパティ名の便利さが少しだけわかるかもしれません。

js

1import { useState } from 'react'; 2 3export default function FormTextarea() { 4 const [form, setForm] = useState({ 5 agreement:true, concent:true 6 }); 7 8const handleFormCheck = e => { 9 setForm({ 10 ...form, 11 [e.target.name]: e.target.checked 12 }); 13}; 14 15 const show = () => { 16 console.log(`同意確認:${form.agreement ? '同意': '反対'}`); 17 }; 18 19 return ( 20 <form> 21 <label htmlFor='agreement'>同意項目:</label> 22 <input id='agreement' name='agreement' type='checkbox' 23 checked={form.agreement} 24 onChange={handleFormCheck} /><br /> 25 <p> (同意しているか? {form.agreement ? '同意している': '反対している'}</p> 26 27 <label htmlFor=''>納得感:</label> 28 29 <input id='concent' name='concent' type='checkbox' 30 checked={form.concent} 31 onChange={handleFormCheck} /><br /> 32 <p> (納得しているか? {form.concent ? '納得している': '不服である。'}</p> 33 <button type='button' onClick={show}>送信</button> 34 </form> 35 ); 36}

この例では、「同意」に加えて「納得」のチェックボックスを追加しています。それぞれ、agreementとconcent という名前を指定しており
各チェックボックスをクリックした結果が、そのチェック状況に応じて表示されます。
handleFormCheck 関数の中身は質問から変えていません。
計算プロパティ名によって自動的にどのチェックボックスがチェックされたのかを判別しています。

(スプレッド構文を使うことの有用性に着目するならば、公式のこの例の方がもっとわかりやすいと思います)

投稿2023/11/29 10:39

編集2023/11/29 15:03
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

KAI

2023/12/07 00:01

とても分かりやすかったです。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問