/ react

Using Redux-Form with Rails API

The last time I played with redux-form it was unstable and I regretted every moment. But since version 6 (now 7) most of the problems were solved. For anyone writing ajax based forms it now offers stability and best practices. Here's how we integrate redux-form with an existing Rails application.

Field Names

The first thing to remember is Rails field names are different in input elements than in the error messages.

Let's assume we have a Person model and a People controller. That people controller probably has the following person_params method defined:

def person_params
      params.require(:person).permit(:first_name, :last_name)
end

So to match the names our form has to use person[...] around each parameter name:

    <form onSubmit={handleSubmit}>
      {error && <div className='form-errors'>{error}</div>}

      <Field
        name="person[first_name]"
        label="First Name"
        component={InputField}
        type="text"
        placeholder="First Name"
        validate={validations.required}
      />

    <Field
      name="person[last_name]"
      component={InputField}
      type="text"
      label="Last Name"
      placeholder="Last Name"
      validate={validations.required}
    />
<div>
  <button type="submit" disabled={pristine || submitting}>Submit</button>
  <button type="button" disabled={pristine || submitting} onClick={reset}>
    Clear Values
  </button>
</div>
    
</form>

Each field name is prefixed by the rails resource name.

Ajax Submit The Form

I used the name SimpleForm as the example form component's name, so to use the form I can write the following render function:

render() {
  return (
    <Provider store={store}>
      <div>
        <Panel />
        <SimpleForm onSubmit={submitResults} />
      </div>
    </Provider>
  );
}

And the main gist of submitting the form is written in submitResults. The function takes a redux-form values object and sends it to the server:

  async function submitResults(values) {
    try {
      await $.ajax({
        method: 'POST',
        data: values,
        url: '/people',
      });
    } catch (error) {
      const serverErrors = translateServerErrors(JSON.parse(error.responseText));
      throw new SubmissionError(serverErrors);
    }
  }

Translating Server Errors

Finally we need to translate Rail's error messages as by default their key is the field name (without the model around it). So for example the JSON.parse(error.responseText) object would have a key first_name to alert us the server found an error with first_name field.

To translate the object I use the following code:

function translateServerErrors(serverErrors) {
  const result = {};
  for (let key of Object.keys(serverErrors)) {
    result[`person[${key}]`] = serverErrors[key].join(', ');
  }
  return Object.assign({}, result, {_error: "Submit Failed"});
}

Adding CSRF Token

The above is enough for Rails API applications, but normal Rails forms also include a CSRF protection token. That token is passed in the form itself and in the page's head.

Using jQuery to send the values, we can add the following snippet to the startup of our application which will automatically add the correct CSRF token to every ajax request:

jQuery.ajaxSetup({
  headers: { 'X-CSRF-Token': jQuery('meta[name="csrf-token"]').attr('content') },
});

The full source code for the example presented here, with a Rails application hosting a React/redux-form client is available on github:
https://github.com/tocodeil/react-course-examples/tree/master/73-redux-forms-server-side-validations/forms_demo