How to setup Flow with create-react-app and Visual Studio Code

Mar 26, 2018 10:22 · 1277 words · 6 minute read JavaScript React VSCode Flow

I’ll cut right to the chase: this article is about setting up Flow in a React application based on create-react-app, using VSCode as a text editor. Here we go.

Prerequisites

  • NodeJS >= 6
  • Visual Studio Code

Step 1 - Create a React application

Let’s start by creating a simple React application.

npm install -g create-react-app
create-react-app todo && cd todo
npm install

This should create all of the boilerplate you need to run a basic React application. Test it out by running:

npm start

You should see a website that looks something like this:

Figure 1

Step 2 - Install Flow

Now let’s add Flow to the application. Fortunately, create-react-app has some built-in support for Flow. It automatically strips type annotations out during compilation. Let’s add the package:

npm install --save-dev flow-bin
./node_modules/.bin/flow init

Running this command should create a “.flowconfig” file in the root. We’ll use this file to configure Flow to work with React.

Let’s test that it worked:

./node_modules/.bin/flow

This should output a message saying, “No errors!”

The message isn’t entirely truthful, because Flow does not automatically run for every file in your application. By default, it requires a “// @flow” comment at the top of a file prior to checking it for issues. There are only “No errors!” because Flow hasn’t processed any files!

Let’s get Flow to process “src/App.js” by adding a “// @flow” line at the top. It should now read something like this:

// @flow

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo}className="App-logo"alt="logo"/>
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

Run Flow again.

./node_modules/.bin/flow

This time, you should see an error:

Figure 2

Great Scott! It worked.

Step 3 - Configuring Flow

I don’t like having to add “// @flow” to the top of every file. My project most likely won’t mix-and-match typed and untyped Javascript, so I want all of my files to get processed by Flow. Fortunately, we can do that by updating the “.flowconfig” file in the root:

[ignore]
.*/node_modules/.*
.*/build/.*

[include]

[libs]

[lints]

[options]
all=true

[strict]

You’ll notice that we’ve done a bit more than just enabling Flow on all of the files. We’ve also told Flow not to check the “node_modules” or “build” folders. This is important, as checking “node_modules” can take an impressively long amount of time, and checking “build” will always fail, since the folder contains the built files.

If you run Flow now, you’ll see quite a few errors. Let’s fix them!

Figure 3

The default React component doesn’t work in Flow. Fortunately, React has its own Component class that supports Flow types that we can use instead. Change the class definition to the following:

type Props = {};

class App extends Component<Props> {
  // ...
}

This tells Flow that the component doesn’t require any properties. Run Flow again and the error should go away. Let’s move onto the next error:

Figure 4

It turns out that create-react-app uses Jest for tests, but it doesn’t include the flow types for Jest. Fortunately, we can include those Flow types by installing them using the handy “flow-typed” utility. Install it:

npm install --save-dev flow-typed jest@20.0.4 jest-cli@20.0.4
./node_modules/.bin/flow-typed install

You’ll notice that we also installed “jest” again. We do this because “flow-typed” reads our package.json in order to figure out what type definitions to download.

We’ve pinned the version to 20.0.4, because at the time of writing this, the latest version of Jest has an issue with the latest version of create-react-app.

Now that we have the types downloaded, we need to tell Flow to read them. Open your “.flowconfig” and update it with the following:

[ignore]
.*/node_modules/.*
.*/build/.*

[include]

[libs]
flow-typed

[lints]

[options]
all=true

[strict]

Now run Flow again, and you should see that we have one fewer error! Let’s fix the next one:

Figure 5

This error tells us that React’s “render” method expects an “Element” type as its second argument, but “document.getElementById(…)” returns either an “Element” or “null”. We can fix this by checking that the root element exists prior to rendering. Update “src/index.js” as follows:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

const root: ?Element = document.getElementById('root');

if (root != null) {
  ReactDOM.render(<App/>, root);
  registerServiceWorker();
}

Run Flow again to verify that the error is fixed. Great! Next:

Figure 6

Environment variables seem to suffer from the same problem. They can be undefined or a string. Let’s make sure the PUBLIC_URL variable is always a string by storing it in a local variable that falls back to the empty string if it doesn’t exist. Add the following to the top of “src/registerServiceWorker.js”:

const PUBLIC_URL: string = process.env.PUBLIC_URL != null
  ? process.env.PUBLIC_URL
  : '';

Now replace every other occurrence of “process.env.PUBLIC_URL” with “PUBLIC_URL”, and then run Flow again. That error will be gone, and now you’ll see this error:

Figure 7

Flow recognizes that the browser might support service workers, but it also recognizes that it might not. The “serviceWorker” object might be undefined! If that’s the case, then this line will break, since “ready” won’t be defined. You can fix this by wrapping all “navigator.serviceWorker.[…]” lines with the following conditional:

if (navigator.serviceWorker != null) {
  // ...
}

And it turns out that all of the remaining errors are due to this issue. Go ahead and wrap the remaining references to “navigator.serviceWorker.[…]” with this conditional, and then re-run Flow. No more errors!

Step 4 - Adding Flow to NPM scripts

Writing out “./node_modules/.bin/flow” becomes tedious very quickly. Let’s fix that by updating our “package.json” to run it automatically. Update your “package.json” with the following:

{
  ...
  "scripts": {
    "start": "$(npm bin)/flow && react-scripts start",
    "build": "$(npm bin)/flow && react-scripts build",
    "test": "$(npm bin)/flow && react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "flow": "$(npm bin)/flow",
    "postinstall": "$(npm bin)/flow-typed install"
  },
  ...
}

Now we can run “npm run flow” to check our errors. Wonderful.

We’ve also added post-install hook that automatically installs flow-typed libraries for new packages after running “npm install”. Also wonderful.

Step 5 - More Flow configuration

React uses a number of ES6 features that require enabling in flow. Let’s take a look at the configuration required to support these features:

[ignore]
.*/node_modules/.*
.*/build/.*

[include]

[libs]
flow-typed

[lints]

[options]
all=true

module.system.node.resolve_dirname=node_modules

esproposal.decorators=ignore
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable

suppress_type=$FlowIssue
suppress_type=$FlowFixMe

[strict]

We tell Flow to:

  • Look in “node_modules” for non-relative package imports.
  • Ignore decorators.
  • Allow static fields.
  • Allow instance fields.
  • Ignore “Flow issue” comments.
  • Ignore “Flow fix me” comments.

You can check out the final code here: https://github.com/FindAPattern/cra-with-flow-sample.git.

And that’s it for Flow configuration. Now let’s set up VSCode.

Step 6 - Setting up VSCode (optional)

Visual Studio Code does not support Flow out-of-the-box, but it does have a number of extensions that support it. These extensions generally conflict with VSCode’s built-in TypeScript support, so we will have to disable that as well.

First, open the “Extensions” tab in VSCode and install the “Flow Language Support” extension from “flowtype”.

Figure 8

Next, open up the “Built-in Plugins” list and disable the following plugins for the workspace:

  • TypeScript and JavaScript Language features
  • TypeScript Language Basics

Finally, tell VSCode that you want to process all project files with Flow by creating a “.vscode” folder in the root of your project, and then creating a “settings.json” file inside of it containing:

{
  "flow.useNPMPackagedFlow": true,
  "flow.runOnAllFiles": true
}

And that’s it! You should now have full IntelliSense support in VSCode for a fully functional React + Flow application.

Happy coding!

Tweet Share

Looking for graphic design help?

I partner with designers at Handshake Studios to provide graphic design feedback to freelancers and early-stage startups.

Sign up at DesignSavior.com!

Subscribe to my newsletter to receive updates about new posts.

* indicates required