質問解決後の追記
suwmn50799さんのご回答にベストアンサーを付けていますが、実質的ベストアンサーはmaisumakunさんのご回答のコメント欄にある、suwmn50799さんの実装例です。
ただし、maisumakunさんにベストアンサーを付けると、suwmn50799さんのteratailのスコアに影響が無いため、suwmn50799さんのご回答にベストアンサーを付けております。
前提
CodeSandBox
上記はデモです。
ユーザーの情報を入力するコンポーネントがあります。
入力欄は最大10まで増やせます。
また、任意の入力欄を削除できます。
実現したいこと
Submitボタンを押した際、No.ごとのname、age、addressそれぞれの値でオブジェクトを生成し、生成したオブジェクトを配列に格納したいです。
例えば、下記のようなオブジェクトの配列を作成したいです。
[ {name: "Suzuki", age: 20, address: "Tokyo"}, {name: "Sato", age: 30, address: "Osaka"}, {name: "Tanaka", age: 40, address: "Fukuoka"} ]
発生している問題・エラーメッセージ
No.ごとの各inputの値を取得できていません。
該当のソースコード
javascript
1import { useState, useRef } from "react"; 2 3const getUniqueStr = () => { 4 return ( 5 new Date().getTime().toString(16) + 6 Math.floor(1000 * Math.random()).toString(16) 7 ); 8}; 9 10export default function App() { 11 const defaultUser = () => { 12 return { 13 id: getUniqueStr(), 14 name: "", 15 age: 0, 16 address: "" 17 }; 18 }; 19 const [users, setUsers] = useState([defaultUser()]); 20 21 const addItem = (e) => { 22 e.preventDefault(); 23 if (users.length < 10) setUsers([...users, defaultUser()]); 24 }; 25 26 const deleteItem = (e) => { 27 e.preventDefault(); 28 const itemId = e.currentTarget.id; 29 const newItems = users.filter((user) => user.id !== itemId); 30 setUsers(newItems); 31 }; 32 33 const list = users.map((user, index) => ( 34 <tr key={user.id} id={`user${index}`}> 35 <td>{index + 1}</td> 36 <td> 37 <input type="text" name="name" /> 38 </td> 39 <td> 40 <input type="number" name="age" /> 41 </td> 42 <td> 43 <input type="text" name="address" /> 44 </td> 45 <td> 46 <button onClick={deleteItem} id={user.id}> 47 Delete 48 </button> 49 </td> 50 </tr> 51 )); 52 53 const formRef = useRef(null); 54 const submitData = (e) => { 55 e.preventDefault(); 56 const formCurrent = formRef.current; 57 console.log(formCurrent); 58 }; 59 60 return ( 61 <form onSubmit={submitData} ref={formRef}> 62 <button onClick={addItem}>Add</button> 63 <button type="submit">Submit</button> 64 <table> 65 <thead> 66 <tr> 67 <th>No.</th> 68 <th>Name</th> 69 <th>Age</th> 70 <th>Address</th> 71 <th>Delete</th> 72 </tr> 73 </thead> 74 <tbody>{list}</tbody> 75 </table> 76 </form> 77 ); 78}
試したこと
上記コードに含めていますが、useRefを使ってtrごとのDOMを取得して、tr以下のinputの値を取得する、という処理を試みました。
javascript
1const submitData = (e) => { 2 e.preventDefault(); 3 const formCurrent = formRef.current; 4 const userData = formCurrent.user0; 5 console.log(userData); 6 ... 7};
userDataはundefindでした。
useRefでname属性なら取得できることは確認できたのですが、trにname属性を付けるとlintに怒られました。
次に下記のように、input要素のname属性から値を取得してみました。
javascript
1const submitData = (e) => { 2 e.preventDefault(); 3 const formCurrent = formRef.current; 4 const inputNames = Array.from(formCurrent.name); 5 const names = inputNames.map(elm => elm.value); 6 console.log(names); 7 ... 8};
これで下記のようにNo.ごとではなくnameごとに値を取得することができました。
[ {"Suzuki"}, {"Sato"}, {"Tanaka"}, ]
同じ要領でageとaddressも配列を作れそうではあります。
しかし、複数の配列からオブジェクトを生成して配列に格納する方法が分かりませんでした。
「javascript 配列 統合」「javascript 配列 合成」などで検索すると、単一の配列にする方法なら分かりました。
Array.prototype.concat() - JavaScript | MDN
また、各inputの値をstateで管理すれば、値を取得できそうではありますが、パフォーマンス的によくないと考えられるため、stateで値を取得する方法はできるだけ避けたいです。
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2021/10/08 21:26 編集
2021/08/09 07:05 編集