Flow + React

Learn how to use Flow with React

Flow supports JSX and has done a lot of work internally to work great with React applications.

Setup Flow with React

Flow and Babel work well together, so it doesn’t take much to adopt as a React user. If you need to setup Babel with Flow, you can follow this guide.

Babel also works out of the box with Create React App, just install Flow and create a .flowconfig.

Typing React with Flow

React has been designed with Flow in mind, so most things take very little effort to type properly.

Adding types for React Component props

You can replace all of your propTypes with a simple props: field in your component classes.

You can also specify defaultProps as a static class property and Flow’s inference will handle the rest for you.

1
2
3
4
5
6
7
8
9
10
class MyComponent extends React.Component {
  props: {
    prop1: string,
    prop2: number,
  };

  static defaultProps = {
    prop1: "foo"
  };
}

Note: You don’t have to mark your defaultProps as optional properties in your props. Flow knows how to handle them properly.

If you cannot use a static class property for defaultProps, you can also add a class field annotation and assign defaultProps after the class declaration.

1
2
3
4
5
6
7
8
9
class MyComponent extends React.Component {
  static defaultProps: {
    prop1: string
  };
}

MyComponent.defaultProps = {
  prop1: "foo"
};

Adding types for React Component state

If you specify state as a class property you can rely on inference to type it correctly.

1
2
3
4
5
6
7
class MyComponent extends React.Component {
  state = {
    foo: 1,
    bar: true,
    baz: 'three',
  };
}

If you cannot use a class property for state, you can also add a class field annotation and assign it in the constructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyComponent extends React.Component {
  state: {
    foo: number,
    bar: boolean,
    baz: string,
  };

  constructor(props) {
    super(props);
    this.state = {
      foo: 1,
      bar: true,
      baz: 'three',
    };
  }
}

Explicitly specifying React Component generics

It can sometimes be useful to explicitly specify the generics of React.Component. You can do this by creating type aliases for your defaultProps, props, and state and passing them as generics to React.Component in that order.

1
2
3
4
5
6
7
8
type DefaultProps = { prop: string };
type Props        = { prop: string };
type State        = { prop: string };

class MyComponent extends React.Component<DefaultProps, Props, State> {
  static defaultProps = { prop: "foo" };
  state = { prop: "bar" };
}

Adding types for React events

1
2
3
4
5
class MyComponent extends React.Component {
  onEvent(event: Event) {
    // ...
  }
}

If you want a more specific type of event you can use one of the derived classes such as MouseEvent.

1
2
3
4
5
class MyComponent extends React.Component {
  onMouseEvent(event: MouseEvent) {
    // ...
  }
}

If you want to add the type of the element you can do so with an intersection type specifying the currentTarget.

1
2
3
4
5
class MyComponent extends React.Component {
  onButtonEvent(event: Event & { currentTarget: HTMLButtonElement }) {
    // ...
  }
}

Note: You should prefer currentTarget over target as target could be any nested element due to DOM event propagation.

Adding types for React refs

All you need to do to type a ref is add a matching class field with the element type (such as HTMLElement or HTMLButtonElement).

1
2
3
4
5
6
7
8
9
import React from 'react';

class MyComponent extends React.Component {
  button: HTMLButtonElement;

  render() {
    return <button ref={el => this.button = el}>Toggle</button>;
  }
}

Adding types to React lifecycle methods

React component lifecycle methods that receive props and state should be typed with the same types as you have provided for the component.

1
2
3
4
5
6
7
8
type Props = { /* ... */ };
type State = { /* ... */ };

class MyComponent extends React.Component<void, Props, State> {
  componentDidUpdate(prevProps: Props, prevState: State) {
    // ...
  }
}

Adding types to React functional components

React’s functional components can be typed the same way any function can be typed in Flow.

1
2
3
4
5
6
7
// @flow
let MyComponent = (props: { foo: string }) => {
  return <div>{props.foo}</div>
};

let a = <MyComponent/>;           // Error!
let b = <MyComponent foo="bar"/>; // Works!
property `foo` Property not found in props of React element `MyComponent`

Building on top of object destructuring these functional components can specify default props with default values.

1
2
3
4
5
6
7
let MyComponent = ({ foo, bar = 2 }: { foo: number, bar?: number }) => {
  return <div>{foo + bar}</div>;
};

let a = <MyComponent/>;                     // Error!
let b = <MyComponent foo={42}/>;            // Works!
let c = <MyComponent foo={42} bar={3.14}/>; // Works!