2
votes

Redux-form Wizard http://redux-form.com/6.7.0/examples/wizard/ works great with standard controls from examples. I was trying to integrate more complex controls, such as react-credit-cards enter link description here and have problems passing its state values into the Wizard redux states onSubmit.

The wizard form is very useful in compiling a list of form data together and OnSubmit sends back the full list of captured form data stored in redux. This type of forms are useful for making registrations, payment informations and surveys etc.

Current behavior?

The use case is to use Wizard sample, and in one of the wizard page bundle with credit card information. The Credit card control is wrapper in the same manner as in renderField class provided int the redux-form examples.

The render class calls the credit card control and create 4 input controls to pass the values of the credit card numbers into the control to draw the creditcard, while copying the data into component state. This is all based on the demo code provided by the react-credit-cards team.

  render() {
    const { name, number, expiry, cvc, focused } = this.state;
    return (
      <div className="creditCardContainer">
        <h1>React Credit Cards</h1>
        <div className="creditCardForm">
          <CreditCards
            number={number}
            name={name}
            expiry={expiry}
            cvc={cvc}
            focused={focused}
            callback={this.handleCallback}
          />
          <form>
            <div>
              <input
                type="tel"
                name="number"
                placeholder="Card Number"
                onKeyUp={this.handleInputChange}
                onFocus={this.handleInputFocus}
              />
              <div>E.g.: 49..., 51..., 36..., 37...</div>
            </div>
            <div>
              <input
                type="text"
                name="name"
                placeholder="Name"
                onKeyUp={this.handleInputChange}
                onFocus={this.handleInputFocus}
              />
            </div>
            <div>
              <input
                type="tel"
                name="expiry"
                placeholder="Valid Thru"
                onKeyUp={this.handleInputChange}
                onFocus={this.handleInputFocus}
              />
              <input
                type="tel"
                name="cvc"
                placeholder="CVC"
                onKeyUp={this.handleInputChange}
                onFocus={this.handleInputFocus}
              />
            </div>
          </form>
        </div>
      </div>
    );
  }

What is the expected behavior?

Need some suggestions as to how to copy the values from the renderCreditCard component states into the Wizard form redux state.

Currently in the Wizard form's onSubmit

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2> {COMPANY} - Sign up form</h2>
        </div>
        <p className="App-intro">
        </p>
        **<WizardForm onSubmit={showResults} />**
        <div className="App-footer">
          <div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0">CC 3.0 BY</a></div>
        </div>
      </div>
    );
  }
}

the redux data return in the callback function does not contain data from the renderCreditCard component injected into Field.

const showResults = values => {

  // append primary key to data
  values["id"] = _.uniqueId();
  values["number"] <- this is currently undefined
  values["names"] <- this is currently undefined
  values["expiry"] <- this is currently undefined
  values["cvc"] <- this is currently undefined
  ...
  });

when it all works, ideally, the values in the callback shall return number, names, expiry and cvc from Wizard form.

I have tried a couple of methods, including converting renderCreditCard.js input control into Field with component="renderField" without success.

Have anyone tried to integrate react-credit-cards into redux-form? especially in Wizard form pattern? what are some of the ways to propagate the credit card component states back into redux-form?

The issue is track in redux-form issue

1

1 Answers

1
votes

What you're looking for can be broken down into three questions

  1. How do I connect the inputs for credit card information to redux-form?
  2. How can I access the credit card information stored by redux-form?
  3. How do I provide that data to the component provided by react-credit-cards?

Let's tackle these piece by piece:


Connecting inputs for credit card information to redux-form

This step is easy, just use the redux-form provided Field component.

// naturally this is within a component that is wrapped with `reduxForm`
render (
  <form>
    <div>
      <Field
        component="input"
        name="number"
        normalize={ this.ensureCcNumberFormatting }
        placeholder="Card number"
        type="tel"
        onFocus={ this.handleInputFocus }
      />
    </div>

    <div>
      <Field
        component="input"
        name="name"
        normalize={ this.ensureCcNameFormatting }
        placeholder="Name"
        type="text"
        onFocus={ this.handleInputFocus }
      />
    </div>

    <div>
      <Field
        component="input"
        name="expiry"
        normalize={ this.ensureCcExpiryFormatting }
        placeholder="Valid Thru"
        type="tel"
        onFocus={ this.handleInputFocus }
      />
    </div>

    <div>
      <Field
        component="input"
        name="cvc"
        normalize={ this.ensureCcCvcFormatting }
        placeholder="CVC"
        type="tel"
        onFocus={ this.handleInputFocus }
      />
    </div>
  </form>
)

Now the fields number, name, expiry and cvc, and the associated user input, should be stored in the redux store under the redux form. reducer. Please note that the functions passed to the normalize props serve the same function as the bindings to the payment library in the react-credit-cards example: they should take the user input and format the credit cards numbers and enforce length limits. More info about normalize here and here.

Also note that the onBlur props should be passed a similar function that is being passed in the react-credit-cards example. redux-form does not inject a prop to the decorated component that provides information on which input field is selected, so you need to maintain this data yourself (See this issue).


Accessing data from redux-form

Just like anything stored in a redux store I would recommend using react-redux and the connect decorator to inject values from your store into components. Happily, redux-form also provides a nifty formValueSelector to use for this purpose.

const selector = formValueSelector('MyFormName')

// returns `{ number: '<data>', name: '<data>', expiry: <data>, cvc: '<data>' }`
const mapStateToProps = state => selector(state, 'number', 'name', 'expiry', 'cvc')

Now you component should have the credit card information available as props.


Injecting necessary data into the component from react-credit-cards

It's easy, just pull the necessary variables from component props or state and inject them into the CreditCards component.

render() {
  const { number, name, expiry, cvc } = this.props
  const { focused } = this.state

  return (
    <form>
      <CreditCards
        number={number}
        name={name}
        expiry={expiry}
        cvc={cvc}
        focused={focused}
        callback={this.handleCallback}
      />
      { /* the fields from the first code example down here */ }
    </form>
  )
}

Hope this helps!