Friday, December 16, 2016

Using Flow types with React components

Continuing series on Flow, in this blog I'm writing about Flow with React. It details advantages of using Flow while building React components.
Blogger: V. Keerti Kotaru . Author of Angular Material book 

Flow is a natural choice for adding types to a React component. It's primarily because Flow and React are both Facebook's open source projects and community is expected to have better traction in that space. Having said that Typescript is a real contender. It is certainly a viable solution to add types to React components and JavaScript. Will plan to write about Typescript with React in future. To read basic details on Flow, a static type checker for JavaScript check out my earlier blogs,  
1. Idiomatic JavaScript with Flow  
2.Working with Flow on Visual Studio Code 

Type checking is not new to React components. PropTypes are often used for Props' validation. Following are couple of advantages with Flow instead of PropsTypes validation
  • Flow adds type validation for entire JavaScript code. Not just Props on a component. PropTypes validation is specific to Props.
  • Workflow - PropType validation errors are normally seen on console. On the other hand Flow extensions for IDEs show validation errors in-line with the code. It's easy to notice type validation errors.
  • Certain validations are detailed with Flow. For example, a validation that a prop is a function could be done with React.PropTypes.func. But Flow can validate return type and parameter types.

Pre-requisites

Install Flow NPM package. Assuming an NPM package is setup for your code repo, install Flow as a dev dependency on the project.

npm install --save-dev flow-bin

(Or install it globally on the machine)

npm install -g flow-bin

Getting Started with Flow for a React application

If you are using babel transpiler for React, you are all set. A Babel plugin babel-plugin-transform-flow-strip-types would remove types while transpiling. It will be installed along with the package babel-preset-react. React projects using Babel would it include it already.

In the Babel configuration file .babelrc  "presets" configuration and value "react" would run the plugin babel-plugin-transform-flow-strip-types

Code Sample

For simplicity I chose counter component. Checkout code sample at this path.  The counter increments or decrements a value every time you click on a button.

For comparison use this link to review a similar component with PropTypes for validating props.

The sample component expects a prop offset. It has a default value 1. Every time + or - button is clicked, would increment or decrement the value by 1. While invoking the component the offset could be provided as a prop, which will override the default value.

Consider following PropType validations

Counter.propTypes = { 
    offset: React.PropTypes.number 
}; 

// default value provided as below. 
Counter.defaultProps = {
    offset: 1
};

Using Flow types for Props

With Flow we can create a type alias for Props as below,
type CounterProps = { 
 offset: number; 


Specify type for the props variable on the constructor

// declare a class variable props of type CounterProps
props: CounterProps

constructor(props: CounterProps){ 
 super(props); 
 // ... 
}

// declare static variable for default props. 
static defaultProps: CounterProps;

// Now that type system is expecting defaultProps on the class, provide a value.
Counter.defaultProps = {
    offset: 1
};

Functions on props

Consider following code snippet. Every time increment or decrement event occurs, a function callback is provided to containing component. In the sample, containing component prints a string with counter data to console. The callback is invoked/called by Counter component

--------------------- main.js ---------------------------

    // While rendering counter print detailed string with time stamp provided by Counter component and counter value.
    ReactDOM.render(
        <Counter offset={4} eventCallback = {(num: number, eventTime: Date) => 
console.log(`At ${eventTime.toString()} Counter Value is ${num} `)} />
        , document.getElementById('reactApp'));

--------------------- Counter.jsx ---------------------------------------
// Counter props with optional eventCallback function declaration. Notice Maybe type highlighted
 type CounterProps = {
    offset: number;
    eventCallback?: (num: number, eventTime: Date) => void;

};


    // While invoking the callback, as the prop is optional, need to verify it's not null.
    increment(): void{
        this.setState({counterValue: this.state.counterValue + this.props.offset});

        // call provided callback function when increment and decrement events occur.
        // While invoking the callback, as the prop is optional, 
        // need to verify it's not null.
        if(this.props.eventCallback != null){
            this.props.eventCallback(this.state.counterValue, new Date());
        }
    }


The callback is an optional parameter. (Notice the ?). Hence Flow enforces null check before using the function.


References and useful links


Flow for React, official documentation - https://flowtype.org/docs/react.html#_