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
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.
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
Code Sample - https://github.com/kvkirthy/VenCKI-Samples/tree/master/Flow-react-sample/Counter-flow-implementation
Flow for React, official documentation - https://flowtype.org/docs/react.html#_