0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ステート変数:分割代入(Destructuring assignment)でのステート宣言の書き方

Last updated at Posted at 2022-08-22

Kindle書籍「Next.jsでつくるフルスタックアプリ 後編 三好アキ著」を読んでの備忘録。

1.ステート変数宣言のシンプルな書き方

各データにつき一つのステートを用意するシンプルな書き方。
ここではname, email, そしてpasswordというステート変数を宣言している。

const Register = () => {
  //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ここ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
  const [name, setName] = useState("")
  const [email, setEmail] = useState("")
  const [password, setPassword] = useState("")
  //↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

    const handleSubmit = async(e) => {
    //(バックエンドに投げてユーザー登録する処理)
      const response = await fetch("http://xxxx.com/user/reg",
        {
        method:"POST",
        body: JSON.stringify({
               name: name,
               email: email,
               password: password,
              }),
        })
    }

    return (
      <div>
        <form onSubmit={handleSubmit}>
          <input value={name} onChange={(e) => setName(e.target.value)} 
              type="text" name="name" required />
          <input value={email} onChange={(e) => setEmail(e.target.value)}
              type="text" name="email" required />
          <input value={password} onChange={(e) => setPassword(e.target.value)}
              type="text" name="password" required />
          <button>登録</button>
        </form>
      </div>
    )
  }
export default Register

ステート関数(setName()setEmail())は、HTMLの<input>タグにonChange={(e) =>setName(e.target.value)とインライン形式で指定され実行される。ありがちなパターン。

2.ステート変数を分割代入でまとめる書き方

次のように、各データ(ステート変数)をひとつのステート変数にまとめてしまう。ここではnewUserにまとめている。

const Register = () => {
  const [newUser, setNewUser] = useState({
    name: "",
    email: "",
    password: "",
})

・・・省略・・・

こうした場合、まず関数を用意し(ここではhandleChange)、その中で次のようにステート変数のデータ書き込みを行う。

const Register = () => {
  const [newUser, setNewUser] = useState({
    name: "",
    email: "",
    password: "",
})

    const handleChange = (e) => {
        //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ここ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
        setNewUser({
            ...newUser,
            [e.target.name]: e.target.value,
        })
        //↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    }

・・・省略・・・

ここで出てくるnewUserの前の点々...分割代入(Destructuring assignment)と呼ばれるらしい。

HTMLの<input>タグのonChange=インラインはもちろんhandleChange関数に変更する。

また、この中で例えばemailにアクセスしたい場合はnewUser.emailとなる。したがって、<input>タグのvalue値も、例えばnameであったらnewUser.name, emailであったらnewUser.emailと変更する。

const Register = () => {
  const [newUser, setNewUser] = useState({
    name: "",
    email: "",
    password: "",
})

    const handleChange = (e) => {
        setNewUser({
            ...newUser,
            [e.target.name]: e.target.value,
        })

    const handleSubmit = async(e) => {
    //(バックエンドにフェッチする処理)
      const response = await fetch("http://xxxx.com/user/reg",
        {
        method:"POST",
        body: JSON.stringify({
               name: name,
               email: email,
               password: password,
              }),
        })
    }

    return (
      <div>
        <form onSubmit={handleSubmit}>
          <input value={newUser.name} onChange={handleChange} 
              type="text" name="name" required />
          <input value={newUser.email} onChange={handleChange}
              type="text" name="email" required />
          <input value={newUser.password} onChange={handleChange}
              type="text" name="password" required />
          <button>登録</button>
        </form>
      </div>
    )
  }
export default Register

こうすることで、複数の項目(nameやemail, password)を含んだnewUserが出来上がる。

さて、バックエンドに送るべきデータはすべてステート変数newUserに格納されているので、バックエンドにデータを投げる処理をするhandleSubmit関数の中の、データ記述部分(body)も変更する。

これを:point_down_tone2:

        body: JSON.stringify({
               name: name,
               email: email,
               password: password,
              }),
        })

このように変更:point_down_tone2:

        body: JSON.stringify(newUser)

完成形

const Register = () => {
  const [newUser, setNewUser] = useState({
    name: "",
    email: "",
    password: "",
})

    const handleChange = (e) => {
        setNewUser({
            ...newUser,
            [e.target.name]: e.target.value,
        })

    const handleSubmit = async(e) => {
    //(バックエンドにフェッチする処理)
      const response = await fetch("http://xxxx.com/user/reg",
        {
        method:"POST",
        body: JSON.stringify(newUser)
    }

    return (
      <div>
        <form onSubmit={handleSubmit}>
          <input value={newUser.name} onChange={handleChange} 
              type="text" name="name" required />
          <input value={newUser.email} onChange={handleChange}
              type="text" name="email" required />
          <input value={newUser.password} onChange={handleChange}
              type="text" name="password" required />
          <button>登録</button>
        </form>
      </div>
    )
  }
export default Register

3.あえてエラーを試してみる(この記事の肝)

    setNewUser({
        ...newUser,
        [e.target.name]: e.target.value,
    })

なんとも不思議な書き方、こういうものだとスルーして良いのだが、いくつか意地悪を試してみる。

  • 【疑問1】点々は3つでないとダメなのか?

【回答】
3つと決まっているようで、2つや4つにするとVS Codeが注意してくれる。
image.png

  • 【疑問2】 [e.target.name]: e.target.valueと、e.target.namee.target.valueを割り当ててるコード部分がある。それを、[e.target.email]: e.target.valueのようにemailpasswordにしてはダメなのか?

【回答】
ダメ。受け付けてくれない。ステート変数宣言部分をこのように:point_down_tone2:順番を入れ替えてもダメ。

  //分割代入
  const [newUser, setNewUser] = useState({
    email: "",
    name: "", 
    password: "",
  })

  • 【疑問3】では、name, email共に二つ書いたり、name, email, password3つともすべて書くと:point_down_tone2:どうなのか?
    setNewUser({
        ...newUser,
        [e.target.name]: e.target.value,
        [e.target.email]: e.target.value,
        [e.target.password]: e.target.value,
    })

【回答】
これではせっかくひとつ(newUser)にまとめた意味がないものの、受け付けてくれる。

  • 【疑問4】入力フォームに「名前」(name)欄が無い場合はどうなのか? 当然ステート変数宣言は次のようなものになるが・・
  const [newUser, setNewUser] = useState({
    email: "",
    password: "",
  })

【回答】
この場合はさすがに[e.target.email]: e.target.valueであろうと推測した。しかしながら、実際はこの場合でも:point_down_tone2:のように[e.target.name]: e.target.valuenameにしなければ受け付けてくれない。

    setNewUser({
        ...newUser,
        [e.target.name]: e.target.value,
    })

4.残った疑問

結局、ここでの[e.target.name]は何を指しているのだろうか。
もしかしたらスキーマであろうか? データベースに格納すべきデータのスキーマを次のように設定している(UserSchema)。しかし、これはバックエンドプログラムがMongoDBにデータを書き込むために用意された物。フロントでは使っていない。

//utils/ShemaModels.js
const UserSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true,
        unique: true
    },
    password: {
        type: String,
        required: true,
    }
})

やはり、不明のままである。:thinking:



本の宣伝

Gatsbyバージョン4>>>>改訂新版

前編の『Gatsby4+Tailwind+Gatsby Cloudでつくる~』と後編の『JAMStackを学ぼうGatsby4+microCMSでつくる~』を合わせ、次のようなデモサイトを構築します。
https://yah-space.work


静的サイトジェネレーターGatsbyの基本とnode APIの扱いについて踏み込んだ解説書。またバージョン4の新機能《DSG(静的サイトジェネレーション》と《Gatsby Functions》での問い合わせフォーム実装、Tailwind CSSでのレイアウト、Gatsbyと相性が良いGatsby Cloudを紹介し解説しております。


JAMStackを学ぼう Gatsby4+Tailwind CSS+Gatsby Cloudでつくるコーポレートサイト ~もうレンタルサーバーはいらない~ 改訂新版 前編(書籍2,980円)



GatsbyとmicroCMSを組み合わせてのコーポレートサイト作成手順を解説・ハンズオンした続編。待望の《サイト内検索機能》を《Gatsby Functions》と《microCMSのqパラメータ》で実装。

JAMStackを学ぼう Gatsby4+microCMSでつくるコーポレートサイト ~WordPressはもう古い~ 改訂新版 後編(書籍 2,580円)



0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?