3
3

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 5 years have passed since last update.

redux-form (5) - Initialize From State

Last updated at Posted at 2019-07-04

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**の説明です。

redux-formの概説についてはまず以下の記事を参考にしてください。

redux-form (1) - Simple Form Example

Initialize From State

Initialize From State - Getting Started With redux-form

redux-formでは、decorated form component(wrapped component)のform stateを初期化するために、initialValuesを用います。initialValuesは、例えばサーバから取得した値を、React Redux の connect()を使って、mapStateToPropsでRedux state をinitialValues propに変換して、decorated form componentに渡すようにします。

initialValuesでformを初期化できるのは、デフォルトで1回のみです。

decorated form componentはreset() propを受け取りますが、reset()を実行することでformをinitialValuesに戻すことができます。

以下が、reducerとaction creatorのソースです。

src/account.js
// Quack! This is a duck. https://github.com/erikras/ducks-modular-redux
const LOAD = 'redux-form-examples/account/LOAD'

const reducer = (state = {}, action) => {
  switch (action.type) {
    case LOAD:
      return {
        data: action.data
      }
    default:
      return state
  }
}

/**
 * Simulates data loaded into this reducer from somewhere
 */
export const load = data => ({ type: LOAD, data })

export default reducer

以下がredux-formのサンプルです。ポイントの説明を下にまとめてあります。

src/InitializeFromStateForm.js
import React from 'react'
import { connect } from 'react-redux'
import { Field, reduxForm } from 'redux-form'
import { load as loadAccount } from './account'

const data = {
  // used to populate "account" reducer when "Load" is clicked
  firstName: 'Jane',
  lastName: 'Doe',
  age: '42',
  anniversaryDate: '2018-08-22',
  sex: 'female',
  employed: true,
  favoriteColor: 'Blue',
  bio: 'Born to write amazing Redux code.'
}

const colors = ['Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Indigo', 'Violet']

let InitializeFromStateForm = props => {
  const { handleSubmit, load, pristine, reset, submitting } = props
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <button type="button" onClick={() => load(data)}>
          Load Account
        </button>
      </div>
      <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>Age</label>
        <div>
          <Field name="age" component="input" type="number" placeholder="Age" />
        </div>
      </div>
      <div>
        <label>Anniversary Date</label>
        <div>
          <Field name="anniversaryDate" component="input" type="date" />
        </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 value="">Select a color...</option>
            {colors.map(colorOption => (
              <option value={colorOption} key={colorOption}>
                {colorOption}
              </option>
            ))}
          </Field>
        </div>
      </div>
      <div>
        <label htmlFor="employed">Employed</label>
        <div>
          <Field
            name="employed"
            id="employed"
            component="input"
            type="checkbox"
          />
        </div>
      </div>
      <div>
        <label>Bio</label>
        <div>
          <Field name="bio" component="textarea" />
        </div>
      </div>
      <div>
        <button type="submit" disabled={pristine || submitting}>
          Submit
        </button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>
          Undo Changes
        </button>
      </div>
    </form>
  )
}

// Decorate with reduxForm(). It will read the initialValues prop provided by connect()
InitializeFromStateForm = reduxForm({
  form: 'initializeFromState' // a unique identifier for this form
})(InitializeFromStateForm)

// You have to connect() to any reducers that you wish to connect to yourself
InitializeFromStateForm = connect(
  state => ({
    initialValues: state.account.data // pull initial values from account reducer
  }),
  { load: loadAccount } // bind account loading action creator
)(InitializeFromStateForm)

export default InitializeFromStateForm

React-Reduxのconnect()関数について少し説明します。

mapStateToPropsinitialValues propが設定されています。initialValues は reduxForm が処理します。initialValues は { field1: 'value1', field2: 'value2' } の形をしており、componentWillMount() において form を初期化します。
initialValuesはここの例のようにpropで受け渡す以外に、reduxForm() のconfig parameterで受け渡す方法もあります。

mapDispatchToPropsobject を指定することができます。この場合、objectの各fieldがaction creatorとなり、 React-Redux は dispatchをaction creatorにbindします。
connect() -React Redux

InitializeFromStateForm = connect(
  state => ({          // mapStateToProps
    initialValues: state.account.data 
  }),
  { load: loadAccount } // mapDispatchToProps
)(InitializeFromStateForm)

以下はindex.jsですが、オリジナルのものを最小化してあります。

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

const dest = document.getElementById('content')

const reducer = combineReducers({
  account,
  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 InitializeFromStateForm = require('./InitializeFromStateForm').default
  ReactDOM.hydrate(
    <Provider store={store}>
      <h2>Form</h2>
      <InitializeFromStateForm onSubmit={showResults} />
    </Provider>,
    dest
  )
}

render()

実行画面

初期画面です

image.png

「Load Account」ボタンでformを初期化します。最初の一回目だけ有効です。初期状態を変更してから、再度初期状態に戻すためには、UndoChangesボタンでreset()を発行する必要があります。

image.png

submitボタンを押します。

image.png

今回は以上です。

3
3
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
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?