React - Flow code static checking

Flow

Flow is Facebook's open source static code inspection tool. Its function is to check the static code of React components and Jsx syntax to find some possible problems before running the code. Flow can be used for all front-end development projects and not only limited to React. Coders can go to the official website to learn more about it (friendly reminder: VPN may be required, which is very unstable). This article only describes how to use it with React development.

Flow is just a tool for inspection. It is very convenient to install and use. Pay attention to the following three points when using it:

  1. Add Flow to our project.
  2. Make sure the compiled code has no Flow related syntax.
  3. Added Flow-related type annotations where checking is required. (Similar to Java's Annotation mechanism)

Next, we will explain the specific content of the above three points one by one. Code friends can operate while reading.

Add Flow to our project

Install the latest version of Flow:

Npm :

npm install --save-dev flow-bin

After the installation is complete, add the execution script to the package.json file:

{
  // ...
  "scripts": {
    "your-script-name": "flow",
    // ...
  },
  // ...
}

Then initialize Flow:

npm run flow init

After the execution is complete, Flow will output the following content in the terminal:

> [email protected] flow /yourProjectPath
> flow "init"

Then generate a file named .flowconfig in the root directory, which looks like this after opening:

[ignore]

[include]

[libs]

[lints]

[options]

[strict]

Basically, the configuration file has no special requirements and does not need to be configured. Flow covers all files after the current directory by default. [include] is used to include files outside the project. E.g:

[include]

../otherProject/a.js

[libs]

It will include the otherProject/a.js file at the same level as the current project. See here for configuration files .

The compiled code removes the Flow related syntax

Flow extends the JavaScript syntax by adding some annotations. Therefore, the browser cannot correctly interpret these Flow-related syntaxes, and we must remove the added Flow annotations in the compiled code (the final released code). The specific method depends on what kind of compilation tool we use. The following will describe some commonly used compilation tools for React development

Create React App

If your project was created directly using Create React App . Then you don't have to worry about removing the Flow syntax. Create React App has already done it for you. Just skip this section.

Babel

Before the 15.x version, most coders who entered the pit of React should use Babel as a syntactic sugar compiler. At that time, Create React App was completely immature. If using Babel we also need to install a Babel preset for Flow:

npm install --save-dev babel-preset-flow

Then, we need to add a Flow-related preset to Babel's configuration file :

{
  "presets": [
    "flow",
    //other config
  ]
}

other methods

If you are neither using Create React App nor Babel as a syntactic sugar compiler, you can use the tool  flow-remove-types to remove Flow code before release.

Run Flow

After completing the above steps, you can start running the flow:

npm run flow

Then you will type something like this:

> [email protected] flow /yourProjectPath
> flow

Launching Flow server for /yourProjectPath
Spawned flow server (pid=10705)
Logs will go to /tmp/flow/zSworkzSchkuizSone-big-website.log
Monitor logs will go to /tmp/flow/zSworkzSchkuizSone-big-website.monitor_log
No errors!

The first run will generate a lot of temporary files which is slow, after that it will be much faster.

Add Flow annotation

If you know C++/C# metaprogramming or Java Annotation, then understanding Flow's Annotation will be very easy. It is probably to add an annotation (Annotation) before the file, method, and code block to inform the execution behavior of Flow.

First, Flow only checks files that contain the // @flow annotation. So if we need to check, we need to write our file like this:

// @flow
import React from 'react'

class MyComponent extends React.Component {
    render(){
        return (<div>MyComponent</div>)
    }
}

export default MyComponent

Then we run Flow again and it becomes this style:

> [email protected] flow /yourProjectPath
> flow

Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:5:21

Cannot use property Component [1] with less than 1 type argument.

     dev/src/home/test.js
      2│
      3│ import React from 'react'
      4│
      5│ class MyComponent extends React.Component {
      6│     render(){
      7│         return (<div>MyComponent</div>)
      8│     }

     /tmp/flow/flowlib_cc1898a/react.js
 [1] 26│ declare class React$Component<Props, State = void> {

At this point, Flow has been successfully installed, and the next thing to write is to add various annotations to strengthen type qualification or parameter detection. The following content will briefly introduce the relevant grammar rules of flow.

React component parameter checking

https://www.chkui.com/article/react/react_typechecking_with_proptypes_and_dom_element introduced that React uses the PropType mechanism to limit the type and range of parameters passed by the user using the component, but PropType is a running detection mechanism. After the program runs, the specific data is checked. Flow is a static check, which is a check before the code is compiled and run, and the two complement each other without interfering with each other.

Props parameter check

Following the example of MyComponent above, we introduce Flow annotations to check the code:

// @flow
// flow的例子,可以看看和PropType的差异在哪
import React from 'react'

type Props = {
    num : number,
    text : ?string
}

//通过<>引入Flow类型检查
//可以直接写成 React.Component<{num : number, text ?: string}>这样的形式
class MyComponent extends React.Component<Props> {
    render(){
        return (<div>{this.props.num}\{this.props.text}</div>)
    }
}

export default MyComponent

Then when running Flow, No Error is output.

Then we use this component:

// @flow
// flow的例子,可以看看和PropType的差异在哪
import React from 'react'

type Props = {
    num : number,
    text : ?string
}

class MyComponent extends React.Component<Props> {
    render(){
        this.props.myValue;
        return (<div>{this.props.num}\{this.props.text}</div>)
    }
}

//void 表示 undefined 不传递参数
//这里传递类型发生错误
const UseComponent = (props : void) =>(<MyComponent num="2" text={2}/>)

export default UseComponent

Output after running flow:

Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:12:20

Cannot get this.props.myValue because property myValue is missing in Props [1].

      9│
 [1] 10│ class MyComponent extends React.Component<Props> {
     11│     render(){
     12│         this.props.myValue;
     13│         return (<div>{this.props.num}\{this.props.text}</div>)
     14│     }
     15│ }


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:17:40

Cannot create MyComponent element because:
 • string [1] is incompatible with number [2] in property num.
 • number [3] is incompatible with string [4] in property text.

    [2]  6│     num : number,
    [4]  7│     text : ?string
          :
        14│     }
        15│ }
        16│
 [1][3] 17│ const UseComponent = (props : void) =>(<MyComponent num="2" text={2}/>)
        18│
        19│ export default UseComponent



Found 3 errors

It can be seen from the output that there are a total of 2 error bar outputs:

  • The first column means that myValue is not declared.
  • The second column [1] violates the qualification of [2], and [3] violates the qualification of [4]. We change the component to <MyComponent num={2} text="2"/> to pass the check.

Add check for State

React's data is controlled in two places - props and  state . Flow also provides state data inspection, we add state inspection in the example:

// @flow
// flow的例子,可以看看和PropType的差异在哪
import React from 'react'

type Props = {
    num : number,
    text : ?string
}

type State = {
    count: number,
};

class MyComponent extends React.Component<Props, State> {
    constructor(...props){
        super(...props)
        this.state = {count:'1'}
    }

    render(){
        return (<div>{this.props.num}\{this.props.text}</div>)
    }
}

const UseComponent = (props : void) =>(<MyComponent num={2} text="2"/>)

export default UseComponent

Running Flow at this point will output:

Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:17:29

Cannot assign object literal to this.state because string [1] is incompatible
with number [2] in property count.

 [2] 11│     count: number,
     12│ };
     13│
     14│ class MyComponent extends React.Component<Props, State> {
     15│     constructor(...props){
     16│         super(...props)
 [1] 17│         this.state = {count:'1'}
     18│     }
     19│
     20│     render(){

Detected the wrong type of assignment of state.count in the constructor.

component defaults

The default value can be used after using Flow, but it must be noted that the type of the default value must be consistent with the annotation declaration:

import * as React from 'react';

type Props = {
  foo: number, 
};

class MyComponent extends React.Component<Props> {
  static defaultProps = {
    foo: 42, 
  };
}

Component of function type

In addition to using the Class keyword, using functions can also construct a React component for use with Flow:

import React from 'react';

type Props = {//参数检查
  foo: number,
  bar?: string,
};

function MyComponent(props: Props) {
  return <div>{props.bar}</div>;
}

MyComponent.defaultProps = {
  foo: 42 //指定默认值
};

React events, subcomponents, higher order component inspection extensions

In addition to basic checks for individual components, Flow also provides support for React events, refs, subcomponents, higher-order components, and Redux. This article will not introduce them one by one. Code friends who need it can follow the following resource list to learn about the relevant content:

type checking extension

Flow checks all JavaScript primitives - Boolean, String, Number, null, undefined (replace void in Flow). In addition, some operation symbols are also provided, such as text : ?string in the example, which indicates that the parameter has "no value". In addition to passing the string type, it can also be null or undefined. It should be noted that the "non" of no value and JavaScript expression here are two concepts. Flow's "no value" only has null and void (undefined), while the "non" of JavaScript expression includes: null, undefined, 0, false.

In addition to the various type parameters given in the previous examples, Flow has richer checking capabilities, see here to learn more.

React Data Type Reference

For Flow, in addition to regular JavaScript data types, React also has its own unique data types. Such as React.Node, React.Key, React.Ref<>, etc. For details, you can check the official website for the description of React types .

It should be noted that if you want to use the type of React, you need to use this method when introducing React objects through ES6:

import * as React from 'react'
//替换 import React from 'react'

//或者单独引入一个类型
//import type {Node} from 'react

The difference between the two lies in the asterisk import feature of ES6. Using the * sign will combine all the export contents in a file into an object and return it. Without the asterisk, you can only get the prototype of exprot default. After the introduction of Flow, the default export type of React will not be modified, because the default export is not necessarily an object, and it will extend more types for React through export.

For example, we use React.Node to restrict the return type of the render method:

import * as React from 'react'
class MyComponent extends React.Component<{}> {
  render(): React.Node {
    // ...
  }
}

some problems encountered

One of the problems I encountered in the process of using it is the usual "./xxx.scss. Required module not found" when importing style resources or pictures. Check the official documentation to know that Flow only supports .js, .jsx, . mjs, .json files, if you need to import other files, you need to extend options. Add options in .flowconfig:

[ignore]
[include]
[libs]
[lints]
[options]
module.file_ext=.scss
[strict]

Also, some IDEs don't support Flow very well. The webstorm 2017.3.5 I am currently using is relatively good, but remember to set the version to Flow in File->Setting->Languages&Frameworks->Javascript.

Write the experience at the end

Introducing and constraining each component according to the Flow specification will lead to a lot of increase in development (of course you don't need to introduce it is another matter, but what do you do if you don't need to introduce it?). Building the framework function of Flow is just the beginning. After that, in addition to the team members need to understand how to use Flow, there will be various pits that need to be solved in the early stage. And Flow is also much  "heavier" than React's PropTypes .

JavaScript was originally a prototyping language for type deduction. When Flow came in, it became more and more like a strongly typed language like Java. I don’t know if it’s good or bad, but Java10 learned JavaScript, etc., and added type inference such as val. Keyword.....

In general, the introduction of specifications has a cost, which depends on the size of the team and the size of the project. It is not that the more technology stacks are introduced, the more compelling they are. If the number of front-end developers in your independent project is not large, or the speed of code expansion (code rot) has not caught you off guard, it is recommended to introduce Flow carefully. In addition to developer self-inspection, Flow is integrated into the entire testing framework to perform centralized inspections before integration tests or a version of the code is released. It is necessary to think about the role it plays in the development, testing, simulation, and online iteration cycles of the project, and even integrate it into management processes like CMMI to reverse quantify the code quality.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325299265&siteId=291194637
Recommended