Reactでフォームの外側あるいは別コンポーネントのbuttonからsubmitする

FormikというReactのフォームライブラリがあり、それでFormを組んでいてハマったのでメモ。

github.com


欲求

Formの中にButtonを配置せず、『外側にサブミットボタンを配置したい』

ようはフォームの外側にonSubmitなボタンコンポーネントを作って、それを押したら目的のフォームがsubmitされるようにしたい。

解決策

github.com

react-final-formのFAQみてたら書いてあった。

① formのDOM取得してEventをディスパッチする

Via document.getElementById()

<button onClick={() => {
  document.getElementById('myForm').dispatchEvent(new Event('submit')) // ✅
}}>Submit</button>

<form id="myForm" onSubmit={handleSubmit}>
  ...fields go here...
</form>

なるほど!

クロージャでsubmt変数を定義してそこにsubmit関数を代入する

Via Closure

let submit
return (
  <div>
    <button onClick={submit}>Submit</button> // ❌ Not overwritten closure value
    <button onClick={event => submit(event)}>Submit</button> // ✅
    <Form
      onSubmit={onSubmit}
      render={({ handleSubmit }) => {
        submit = handleSubmit
        return <form>...fields go here...</form>
      }}
    />
  </div>
)

たしかに〜

③ そもそもHTML5のform属性を利用できる

これは別途react-final-formのFAQには書いてないけどやってみたらできた別案。

HTML5にはform属性があって、例えばMDNのbuttonのページにはこうある

ボタンに関連付けられた form 要素(form owner)です。 属性値は同一文書内の

要素の id 属性と同一の値にしなくてはなりません。 この属性を指定しない場合は、祖先に 要素が存在すれば、その要素に関連付けられます。 この属性によって 要素の子孫にするだけでなく、同一文書内にある任意の <form> 要素に <button> 要素を関連付けることが可能になりました。

ようするにformのid属性と関連付けが出来る

ので、こう書けばformの中にbuttonがなくともsubmitが可能

<button form='myform'>Submit</button>

<form id="myForm" onSubmit={handleSubmit}>
  ...fields go here...
</form>

ReactでFormむずかしい

アプリっぽいUIを作っていて、フォームの送信ボタンをヘッダーの右上(スマホの右上)に設置したくて悩んでいて、この解決方法になんとかたどり着いた。

Reactでフォーム組むのとにかく難しくて、どこに処理書けばいいとかcontrolledかuncontrolledかとかバリデーションとかいろいろある。様々な概念とライブラリの仕様が絡み合うから厳しい気持ちになる。