LoginSignup
9
10

More than 3 years have passed since last update.

redux-form (1) - Simple Form Example

Last updated at Posted at 2019-06-29

redux-form (1) - Simple Form Example
redux-form (2) - Synchronous Validation Example
redux-form (3) - Field-Level Validation Example
redux-form (4) - Submit Validation Example
redux-form (5) - Initialize From State
redux-form (6) - ユーザ登録


ReactでForm componentを作るときに、とても便利なredux-formの説明です。

【注意】(2019/07/13)

いろいろ試した結果、redux-formとreact-router v4の組み合わせがうまく動作しないという結論に達しました。reduxForm()とconnect()という2つのHOCで2重にラップした時に、propsが、最終的なコンポーネントにうまく伝わっていかない感じです。ラップする順番を変えてもだめでした。しかしreduxForm()の代わりに、antdのForm.create()でラップすればうまくいきました。何か私の見落としがあるのかもしれませんが、当面はForm.create()でいろいろ試していきたいと思います。

1. redux-form概説

redux-form はReact form componentをRedux storeにconnectするためのものです。使い方は簡単で、提供されたreducerを使います。actionを明示的に指定する必要もありません。具体的には、次の3つのキー要素が使われます。

  • formReducer : Redux actionによって伝えられた Form の更新を、Redux stateに反映させるreducer。
  • reduxForm() : HOC。 form component (decorated form componentまたはwrapped componentと呼ばれる)をwrapして、ユーザ入力をRedux actionにbindします。
  • <Field /> : wrapped form componentで使われるcomponentで、input componentをRedux-form に組み込んでくれます。

「redux-formが生成するprop一覧」
「reduxFormがwrapped componentに渡せるprop一覧」

reduxFormがpropをwrapped componentに渡す方法は次の2つです:

  • reduxForm(config:Object)のconfigで指定する。
  • 親のcomponentのpropとして指定する。

1-1.redux-formの4ステップ

redux-formを使うためには、基本的に以下の4ステップを踏むことになります。

  • (1) form reducerの初期化
  • (2) reduxForm で Form component を decorate
  • (3) <Field/> を使ってForm content を定義
  • (4) onSubmit関数を定義してForm componentに渡す

この4ステップは実際に、以下に示す「4.Simple Form Example」のソースコードで確認できます。

2. handleSubmitについて

handleSubmitはsubmit時にキックされるハンドラです。

handleSubmit は decorated form component (wrapped component) のpropとしてredux-formが生成するものです。

onSubmitハンドラとしてのhandleSubmitについて

  • まずvalidationを行います。(syncとasyncの両方で)
  • 成功すればonSubmit関数を呼びます。引数としてvalues(formのコンテンツ)が与えられます。
  • onSubmitがpropとして指定されていない場合は、handleSubmit()の引数として与える必要があります。 例:<form onSubmit={handleSubmit(submit)}>
  • onSubmitがpromiseの場合、resolved または rejectedが決定するまでは、submitting propがtrueとなる。

handleSubmitはwrapped componentの中で以下のように取り出せます。

const SimpleForm = props => {
  const { handleSubmit, pristine, reset, submitting } = props

handleSubmitはwrapped componentの中で以下のように使用されます。

<form onSubmit={handleSubmit}>
<button onClick={handleSubmit}>

3. Field について

Redux Form/API/Field

Field componentは、個別のinputをどのようにRedux storeにconnectすべきかを示すものです。以下の3点が重要です。

  • (1) name propが必要とされます。例 'firstName'
  • (2) component propが必要とされます。次の3パターンがあります。1. stateful class component / 2. stateless function component / 3. (3) DOM input string (input, select, or textarea)
  • その他の全てのpropは、component propの指定で生成された要素に渡されます。(★)

3番目に関連して、<Field />がcomponent propのcomponentに渡すpropは次の3種類です。

  • input object
  • meta object
  • custom props (★で述べたprops)

input object

input object は、<input /> componentとReduxを結びつけるためのものです。input object は <input /> componentの中で分解されて使われます。

使用例として、以下の記事のコードを見てみましょう。
redux-form (2) - Synchronous Validation Example

// *** component prop : 2. A stateless function
const renderField = ({
  input, // ★ input object は \<Field /> により component propのcomponentに渡される
  label,
  type,
  meta: { touched, error, warning }
}) => (
  <div>
    <label>{label}</label>
    <div>
      // ★ input objectはここで分解される
      <input {...input} placeholder={label} type={type} />
      {touched &&
        ((error && <span>{error}</span>) ||
          (warning && <span>{warning}</span>))}
    </div>
  </div>
)


const SyncValidationForm = props => {
  const { handleSubmit, pristine, reset, submitting } = props
  return (
    <form onSubmit={handleSubmit}>
      <Field
        name="username"
        type="text"
        // ★ 2. renderField = stateless function component
        component={renderField}
        label="Username"
      />
      <Field name="email" type="email" component={renderField} label="Email" />
      <Field name="age" type="number" component={renderField} label="Age" />
      <div>
        <button type="submit" disabled={submitting}>
          Submit
        </button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>
          Clear Values
        </button>
      </div>
    </form>
  )
}

input objectは次のような属性を持っています。

  • input.checked
  • input.name
  • input.onBlur(eventOrValue)
  • input.onChange(eventOrValue)
  • input.onDragStart(event)
  • input.onDrop(event)
  • input.onFocus(event)
  • input.value

input.valueの値はboolean(checkboxes) や 文字列(他の全てのinput types)です。Redux stateに値が無い場合は 空文字('')となります。null や undefinedではなく、空文字で初期化されるということは、このinput componentがcontrolledであることを保証してくれます。他のtype(Date や Number) が必要な場合はinitialValuesで明示的に指定する必要があります。

4.Simple Form Example

exampleのSimple Form Exampleを見てみましょう。

Simple Form Example - Getting Started With redux-form

index.jsはオリジナルのものから、不要なものを削除してあります。

src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, combineReducers } from 'redux'
// *** redux-form の reducer
import { reducer as reduxFormReducer } from 'redux-form'

const dest = document.getElementById('content')
const reducer = combineReducers({
  form: reduxFormReducer // mounted under "form"
})
const store = createStore(reducer)

const showResults = values =>
  new Promise(resolve => {
    setTimeout(() => {
      // simulate server latency
      window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`)
      resolve()
    }, 500)
  })

let render = () => {
  const SimpleForm = require('./SimpleForm').default
  ReactDOM.render(
    <Provider store={store}>
      <h2>Form</h2>
      <SimpleForm onSubmit={showResults} />
    </Provider>,
    dest
  )
}

render()

<Field />で指定したinputに入力すると自動的にRedux stateと同期を取ってくれます。actionが裏で動いていますが、明示的な指定はありません。formをsubmitすれば、呼び出し元のonSubmit propで指定したhandlerがキックされます。

src/SimpleForm.js
import React from 'react'
import { Field, reduxForm } from 'redux-form'

const SimpleForm = props => {
  const { handleSubmit, pristine, reset, submitting } = props
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>First Name</label>
        <div>
          <Field
            name="firstName"
            component="input"
            type="text"
            placeholder="First Name"
          />
        </div>
      </div>
      <div>
        <label>Last Name</label>
        <div>
          <Field
            name="lastName"
            component="input"
            type="text"
            placeholder="Last Name"
          />
        </div>
      </div>
      <div>
        <label>Email</label>
        <div>
          <Field
            name="email"
            component="input"
            type="email"
            placeholder="Email"
          />
        </div>
      </div>
      <div>
        <label>Sex</label>
        <div>
          <label>
            <Field
              name="sex"
              component="input"
              type="radio"
              value="male"
            />{' '}
            Male
          </label>
          <label>
            <Field
              name="sex"
              component="input"
              type="radio"
              value="female"
            />{' '}
            Female
          </label>
        </div>
      </div>
      <div>
        <label>Favorite Color</label>
        <div>
          <Field name="favoriteColor" component="select">
            <option />
            <option value="ff0000">Red</option>
            <option value="00ff00">Green</option>
            <option value="0000ff">Blue</option>
          </Field>
        </div>
      </div>
      <div>
        <label htmlFor="employed">Employed</label>
        <div>
          <Field
            name="employed"
            id="employed"
            component="input"
            type="checkbox"
          />
        </div>
      </div>
      <div>
        <label>Notes</label>
        <div>
          <Field name="notes" component="textarea" />
        </div>
      </div>
      <div>
        /*** 現在の form value が initialValuesに等しいか、または     ***
         *** submit (Promise)がまだresolveしていなければ、ボタンは無効 ***/
        <button type="submit" disabled={pristine || submitting}>
          Submit
        </button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>
          Clear Values
        </button>
      </div>
    </form>
  )
}

export default reduxForm({
  form: 'simple' // a unique identifier for this form
})(SimpleForm)

4-1.入力途中のスナップショット

image.png

4-2.Submit直後のスナップショット

image.png

今回は以上です

9
10
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
9
10