🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

React.js

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

Q&A

解決済

2回答

571閲覧

[React] Cloud Firestoreにupdateする際、フォームを編集しないと空の値で更新されてしまう

DaisukeMori

総合スコア226

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

React.js

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

0グッド

0クリップ

投稿2023/07/17 01:28

不明点

ReactからCloud Firestoreにupdate(更新)する際、フォームに何かしら編集したら問題なく更新できるのですが、何もフォームをいじらずにsubmitすると、値が空になって更新されてしまいます。

理屈を仮定すると何もonChangeしてないからeの引数に何も入らずsetNameのe.target.valueが自ずと空になるから空の値が更新されてしまうのかなと思いますが、この問題を解決する方法が全くわからず困っています。

onChangeしない場合、今入っている値をそのまま更新する方法はありますか?

jsx

1<h2>編集フォーム</h2> 2 <form onSubmit={handleEditFormSubmit}> 3 <input 4 type="text" 5 value={name !== '' ? name : createUser?.name || ''} 6 placeholder="ユーザー名" 7 onChange={(e) => setName(e.target.value)} 8 /> 9 10 <button type="submit">編集</button> 11 </form>

コード全文

jsx

1import { useState } from 'react'; 2import firebase, { db } from '../../Firebase'; 3import Styles from './css/User.module.css'; 4import { useForm } from 'react-hook-form'; 5import { useCollectionData } from 'react-firebase-hooks/firestore'; 6import { UserTypes } from '../../types/types'; 7 8export const User = () => { 9 const [name, setName] = useState(''); 10 const [pending, setPending] = useState(false); 11 const [submitted, setSubmitted] = useState(false); 12 const { register, formState: { errors }, handleSubmit } = useForm(); 13 14 const user = firebase.auth().currentUser; 15 let authId: string | undefined; 16 17 if (user != null) { 18 user.providerData.forEach(() => { 19 authId = user.uid; 20 }); 21 } 22 23 const OnSubmit = async () => { 24 setPending(true); 25 try { 26 await firebase.firestore().collection('users').add({ 27 authId, 28 name, 29 }); 30 setSubmitted(true); 31 } finally { 32 setPending(false); 33 } 34 }; 35 36 const [list, loading, error] = useCollectionData<UserTypes>(db.collection('users'), { idField: 'docId' }); 37 if (loading) return <div>Loading...</div>; 38 if (error) return <div>Error...</div>; 39 40 const createUser = list?.find((user) => user.authId === authId); 41 42 const handleEditFormSubmit = async (event: React.FormEvent) => { 43 event.preventDefault(); 44 setPending(true); 45 try { 46 if (createUser) { 47 await firebase.firestore().collection('users').doc(createUser.docId).update({ 48 name, 49 }); 50 setSubmitted(true); 51 window.location.reload(); 52 } 53 } catch (error) { 54 console.error('Error updating user:', error); 55 } finally { 56 setPending(false); 57 } 58 }; 59 60 return ( 61 <section className={Styles.user}> 62 {!createUser ? ( 63 <div> 64 <h2>登録フォーム</h2> 65 <form onSubmit={handleSubmit(OnSubmit)}> 66 <input 67 {...register('name', { required: true })} 68 value={name} 69 onChange={(e) => setName(e.target.value)} 70 placeholder="ユーザー名" 71 name="name" 72 /> 73 {errors.name?.type === 'required' && 'お名前が入力されていません'} 74 75 <button type="submit">登録</button> 76 {pending && 'Pending...'} 77 </form> 78 </div> 79 ) : ( 80 <div> 81 <h2>編集フォーム</h2> 82 <form onSubmit={handleEditFormSubmit}> 83 <input 84 type="text" 85 value={name !== '' ? name : createUser?.name || ''} 86 placeholder="ユーザー名" 87 onChange={(e) => setName(e.target.value)} 88 /> 89 90 <button type="submit">編集</button> 91 </form> 92 {submitted && <p>更新しました</p>} 93 </div> 94 )} 95 </section> 96 ); 97}

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

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

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

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

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

guest

回答2

0

失礼します、 name, pending, submitted は共に react-hook-form が管理してくれる状態なので、自力で管理する必要はありません(むしろ複雑化するので、重複した状態の管理はすべきではありません。Single Source of Truth 原則です。)

https://react-hook-form.com/get-started を確認して、「状態を管理して submit 時にその状態の値を取り出す」方法を確認しましょう。

https://ja.react.dev/learn/preserving-and-resetting-state が今回の defaultValuesと関係があるのでご確認するのをオススメします。(それだけでなく、このような込み入った画面をスパゲッティ化せずに構築するためには、ステートの扱いの勘所を身に着ける必要があるので、上記サイトを使って基礎を固めましょう。)

tsx

1export const UserEditForm = ({ defaultValues }) => { // フォームの初期値は Props で受け取る 2 const [name, setName] = useState(''); // 不要 3 const [pending, setPending] = useState(false); // 不要 4 const [submitted, setSubmitted] = useState(false); // 不要 5 const { 6 register, 7 formState: { errors, isSubmitting, isSubmitSuccessful }, 8 handleSubmit 9 } = useForm({ defaultValues }); 10 11 const onSubmit: SubmitHandler<{ name: string }> = (data) => console.log(data)

tsx

1export const UserEditPage = () => { 2 const [users, loading, error] = useCollectionData<UserTypes>(db.collection('users'), { idField: 'docId' }); 3 if (loading) return <div>Loading...</div>; 4 if (error) return <div>Error...</div>; 5 6 const curretUser = /* users データ からうまいこと目当てのデータを取ってきてください */; 7 8 const mode = currentUser ? "編集モード" : "新規モード"; 9 10 // key を使うことで、 mode が変わるたびに form ごとまとめて UserEditForm をリセットできる 11 return <UserEditForm key={mode} defaultValues={curretUserData} /> 12} 13 14

投稿2023/09/16 07:25

編集2023/09/16 07:27
honey32

総合スコア228

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

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

0

自己解決

原因

const [name, setName] = useState('');

nameにはフォーム入力がなければuseStateの初期値である空文字が入っているから
当然setNameにも空文字が入りDBに空文字として再更新がかかる。

解決方法

ロジック側でDBの値を取得
const initialName = createUser?.name || '';
もしフォームに入力(useStateに更新)があればその値をDBに、なければ元々の値を入れるようにする

tsx

1await firebase.firestore().collection('users').doc(createUser.docId).update({ 2 name: name !== '' ? name : initialName, 3});

コード全文

tsx

1import { useState } from 'react'; 2import firebase, { db } from '../../Firebase'; 3import Styles from './css/User.module.css'; 4import { useForm } from 'react-hook-form'; 5import { useCollectionData } from 'react-firebase-hooks/firestore'; 6import { UserTypes } from '../../types/types'; 7 8export const User = () => { 9 const [name, setName] = useState(''); 10 const [pending, setPending] = useState(false); 11 const [submitted, setSubmitted] = useState(false); 12 const { register, formState: { errors }, handleSubmit } = useForm(); 13 14 // Firebaseでログインしたユーザーのuidを取得 15 const user = firebase.auth().currentUser; 16 let authId: string | undefined; 17 if (user != null) { 18 user.providerData.forEach(() => { 19 authId = user.uid; 20 }); 21 } 22 23 // 新規ユーザー登録フォーム 24 const OnSubmit = async () => { 25 setPending(true); 26 try { 27 await firebase.firestore().collection('users').add({ 28 authId, 29 name, 30 }); 31 setSubmitted(true); 32 } finally { 33 setPending(false); 34 } 35 }; 36 37 // 新規ユーザー登録処理後 38 const [list, loading, error] = useCollectionData<UserTypes>(db.collection('users'), { idField: 'docId' }); 39 if (loading) return <div>Loading...</div>; 40 if (error) return <div>Error...</div>; 41 42 // ログイン認証IDと新規登録でアップしたIDが一致する情報を取得 43 const createUser = list?.find((user) => user.authId === authId); 44 45 // 編集フォームの初期値はDBのデータで保存 46 const initialName = createUser?.name || ''; 47 48 // 新規登録後の編集フォーム 49 const handleEditFormSubmit = async (event: React.FormEvent) => { 50 event.preventDefault(); 51 setPending(true); 52 try { 53 if (createUser) { 54 await firebase.firestore().collection('users').doc(createUser.docId).update({ 55 name: name !== '' ? name : initialName, 56 }); 57 setSubmitted(true); 58 window.location.reload(); 59 } 60 } catch (error) { 61 console.error('Error updating user:', error); 62 } finally { 63 setPending(false); 64 } 65 }; 66 67 return ( 68 <section className={Styles.user}> 69 {!createUser ? ( 70 <div className={Styles.form}> 71 <h2>プロフィール登録</h2> 72 <form onSubmit={handleSubmit(OnSubmit)}> 73 <label>ユーザー名</label> 74 <input 75 {...register('name', { required: true })} 76 className={Styles.input} 77 type="text" 78 name="name" 79 value={name} 80 placeholder="ユーザー名" 81 onChange={(e) => setName(e.target.value)} 82 /> 83 <p className={Styles.error}>{errors.name?.type === 'required' && 'お名前が入力されていません'}</p> 84 85 <button className={Styles.submitBtn} type="submit">登録</button> 86 {pending && 'Pending...'} 87 </form> 88 </div> 89 ) : ( 90 <div className={Styles.form}> 91 <h2>プロフィール更新</h2> 92 <form onSubmit={handleEditFormSubmit}> 93 <label>ユーザー名</label> 94 <input 95 className={Styles.input} 96 type="text" 97 name="name" 98 value={name !== '' ? name : initialName} 99 placeholder="ユーザー名" 100 onChange={(e) => setName(e.target.value)} 101 /> 102 103 {submitted && <p className={Styles.update}>更新しました</p>} 104 <button className={Styles.submitBtn} type="submit">更新</button> 105 </form> 106 </div> 107 )} 108 </section> 109 ); 110}

投稿2023/07/17 06:17

編集2023/07/17 06:19
DaisukeMori

総合スコア226

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問