Redux-Form with FlowType

Redux-Form with FlowType

By Guillaume Claret

At OuiCar, we use the popular Redux-Form library to handle our forms in React and Redux. To get more confidence in our code, we use the FlowType type checker from Facebook. However, Redux-Form is a pure JavaScript library without Flow annotations. We present here how we managed to add some Flow types on Redux-From.

Types for the React properties

Let us say that we have a form to describe the equipments of a car (in French):

Equipments form

We represent this form with a component Equipments connected to Redux-Form:

class Equipments extends Component<void, Props, void> {
  render(): Element {
    return ...
  }
}

export default reduxForm({
  fields: [
    'description',
    'equipments'
  ],
  ...
}, state => ({
  myStore: ...
}))(Equipments);

We define the type Props of the properties of the component Equipments with our utility UtilReduxForm (see below):

import * as UtilReduxForm from 'ouicar/util/redux-form';

type Props = {
  dispatch: (action: mixed) => void,
  fields: {
    description: UtilReduxForm.field<string>,
    equipments: UtilReduxForm.field<bool[]>
  },
  myStore: ...
} & UtilReduxForm.props;

Doing so we describe the properties of our component following the API of Redux-Form. We explicitly declare the types of our fields by listing each field with its linked type UtilReduxForm.field<A>. In our example, the description field is an input and thus is of type UtilReduxForm.field<string>. The equipments field is an array of checkboxes, so it can be taken for an array of booleans and is of type UtilReduxForm.field<bool[]>. Using the intersection type &, we append the UtilReduxForm.props properties to our list of props.

Now, instead of going to the Redux-Form documentation, we can directly get the list of available properties in the completion list of the Nuclide plugin for Atom, and get some static typing to improve the robustness of our form:

Nuclide infer

The UtilReduxForm utility

Our UtilReduxForm utility contains the declarations of the Flow types for Redux-Form. Here is the full content of our utility:

export type field<A> = {
  active: bool,
  dirty: bool,
  error: ?string,
  initialValue: ?A,
  invalid: bool,
  name: string,
  onBlur: (valueOrEvent: A | SyntheticEvent) => void,
  onChange: (valueOrEvent: A | SyntheticEvent) => void,
  onDragStart: () => void,
  onDrop: () => void,
  onFocus: () => void,
  pristine: bool,
  touched: bool,
  valid: bool,
  value: ?A,
  visited: bool
};

export type props = {
  active: ?string,
  asyncValidate: Function,
  asyncValidating: bool | string,
  destroyForm: () => void,
  dirty: bool,
  error: ?string,
  formKey: ?string,
  handleSubmit: (
    eventOrSubmit: SyntheticEvent |
      (values: Object, dispatch: (action: any) => void) => Promise
  ) => void,
  initializeForm: (data: Object) => void,
  invalid: bool,
  pristine: bool,
  resetForm: () => void,
  submitting: bool,
  submitFailed: bool,
  touch: (...field: Array<string>) => void,
  touchAll: () => void,
  untouch: (...field: Array<string>) => void,
  untouchAll: () => void,
  valid: bool
};

Future work

One major limitation of this approach is that we need to trust that our type declarations correspond to the props given by Redux-Form. We could solve this problem by typing the whole Redux-Form library (which may be a lot of work), or by adding some checks to assert at runtime that the shape of the props is correct.

OuiCar's Picture

About OuiCar

OuiCar is a community market place to find and rent cars anywhere in France. But we are not just car renters. We also like to experiment new technologies and share ideas with others.

Paris http://www.ouicar.fr

Comments