RoadMovie

write down memos or something I found about tech things

redux-formでwizard実装

最近onBoardingページにReact.jsをredux, redux-formと使い始めました。 今回はその際に私が躓いた3つのポイントを紹介します。

  1. デフォルト値の設定方法
  2. ラジオボタンを使ったラベル選択方法
  3. ユーザーが次のフォームに移った時にどうやって自動的に値を設定するか

ちなみに開発環境は下記になります

  • "react": "15.4.2"
  • "redux": "3.6.0"
  • "redux-form": "6.4.3"

デフォルト値の設定方法

もしデフォルトのフォームを使っているのであれば、単純にinitialValueを使って実現できます。

const FirstForm = (props) -> (
  <div>
    ...
  </div>
)

export default reduxForm({
  form: "wizard",
  initialValue: { name: "My Name" }
})

しかし、redux-formのwizardを利用している場合、少し厄介なことになります。wizard formでは、複数個のformが出現しますが、値をイニシャライズするタイミングが最初の一度しかないからです。各フォームが出現するたびに何度もイニシャライズを走らせ直して値を再設定する方法もありますが、少し気持ち悪いですよね。結果として、単純ですが、各Componentの初期化タイミングで設定するとうまく行きます。具体的には componentWillMount() で設定してます。

実装はこんな感じです。

class FirstForm extends React.Component {
  componentWillMount() {
    this.props.change("form-name", "value");
  }
}

export default reduxForm({
  form: 'wizard',                 // <------ same form name
  destroyOnUnmount: false,        // <------ preserve form data
  forceUnregisterOnUnmount: true,  // <------ unregister fields on unmount
  validate
})(FirstForm);

ラジオボタンを使ったラベル選択方法

ラジオボタンをカスタマイズしたい時によくぶつかる問題です。これは単純に下記のように実装できます。

const setSelected = element => {
  document.getElementById(element.currentTarget.htmlFor).checked = true
  element.currentTarget.className = "selected"

  return false
}

export default setSelected;

そして、onClickメソッドとしてこのメソッドを呼び出します。

<label htmlFor="form-name" onClick={setSelected}>
...
</label>
<Field name="form-name" className="hidden">

ユーザーが次のフォームに移った時にどうやって自動的に値を設定するか

私は電話番号の入力に react-phone-input というライブラリを使用しました。この時、2つのフォームを用意しました。1つ目が、このライブラリが返してくるユーザーインプット。もうひとつが、redux-formが提供するhidden formです。 ユーザーが何かを入力すると、そのhidden-formを更新することになります。

setPhoneNumber = () => {
  var phoneNumber = document.getElementsByClassName("react-tel-input")[0].children[0].value
  this.props.change("phone_number", phoneNumber)
}

...

<ReactPhoneInput
  defaultCountry="de"
  onChange={() => this.setPhoneNumber()}
/>
<Field name="phone_number" type="text" component={renderPhoneNumberField} className="hidden" />

こんな感じになります。redux-formは便利ですが癖があるのでよくdocumentを読むことをおすすめします。