Adding Flow types to your React components is incredibly powerful. After typing
your component, Flow will statically ensure that you are using the component in
the way it was designed to be used.
Early in React’s history the library provided PropTypes
which performed
basic runtime checks. Flow is much more powerful as it can tell you when you are
misusing a component without running your code.
There are some Babel plugins which will generate PropTypes
from Flow types
such as babel-plugin-react-flow-props-to-prop-types
if you want both
static and runtime checks.
Class Components
Before we show how to type a React class component with Flow, let us first show
how you would write a React class component without Flow but with React’s prop
types. You would extend React.Component
and add a static propTypes
property.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import React from 'react';
import PropTypes from 'prop-types';
class MyComponent extends React.Component {
static propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
};
render() {
return <div>{this.props.bar}</div>;
}
}
|
{"value":"import React from 'react';\nimport PropTypes from 'prop-types';\n\nclass MyComponent extends React.Component {\n static propTypes = {\n foo: PropTypes.number.isRequired,\n bar: PropTypes.string,\n };\n\n render() {\n return <div>{this.props.bar}</div>;\n }\n}\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":1,"start":7,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":13,"end":17},{"type":"T_STRING","context":"normal","value":"'react'","line":1,"start":18,"end":25},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":25,"end":26},{"type":"T_IMPORT","context":"normal","value":"import","line":2,"start":27,"end":33},{"type":"T_IDENTIFIER","context":"normal","value":"PropTypes","line":2,"start":34,"end":43},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":2,"start":44,"end":48},{"type":"T_STRING","context":"normal","value":"'prop-types'","line":2,"start":49,"end":61},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":61,"end":62},{"type":"T_CLASS","context":"normal","value":"class","line":4,"start":64,"end":69},{"type":"T_IDENTIFIER","context":"normal","value":"MyComponent","line":4,"start":70,"end":81},{"type":"T_EXTENDS","context":"normal","value":"extends","line":4,"start":82,"end":89},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":4,"start":90,"end":95},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":95,"end":96},{"type":"T_IDENTIFIER","context":"normal","value":"Component","line":4,"start":96,"end":105},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":106,"end":107},{"type":"T_STATIC","context":"normal","value":"static","line":5,"start":110,"end":116},{"type":"T_IDENTIFIER","context":"normal","value":"propTypes","line":5,"start":117,"end":126},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":127,"end":128},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":129,"end":130},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":6,"start":135,"end":138},{"type":"T_COLON","context":"normal","value":":","line":6,"start":138,"end":139},{"type":"T_IDENTIFIER","context":"normal","value":"PropTypes","line":6,"start":140,"end":149},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":149,"end":150},{"type":"T_IDENTIFIER","context":"normal","value":"number","line":6,"start":150,"end":156},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":156,"end":157},{"type":"T_IDENTIFIER","context":"normal","value":"isRequired","line":6,"start":157,"end":167},{"type":"T_COMMA","context":"normal","value":",","line":6,"start":167,"end":168},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":7,"start":173,"end":176},{"type":"T_COLON","context":"normal","value":":","line":7,"start":176,"end":177},{"type":"T_IDENTIFIER","context":"normal","value":"PropTypes","line":7,"start":178,"end":187},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":187,"end":188},{"type":"T_IDENTIFIER","context":"normal","value":"string","line":7,"start":188,"end":194},{"type":"T_COMMA","context":"normal","value":",","line":7,"start":194,"end":195},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":198,"end":199},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":199,"end":200},{"type":"T_IDENTIFIER","context":"normal","value":"render","line":10,"start":204,"end":210},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":210,"end":211},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":211,"end":212},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":213,"end":214},{"type":"T_RETURN","context":"normal","value":"return","line":11,"start":219,"end":225},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":11,"start":226,"end":227},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"div","line":11,"start":227,"end":230},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":11,"start":230,"end":231},{"type":"T_LCURLY","context":"normal","value":"{","line":11,"start":231,"end":232},{"type":"T_THIS","context":"normal","value":"this","line":11,"start":232,"end":236},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":236,"end":237},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":11,"start":237,"end":242},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":242,"end":243},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":11,"start":243,"end":246},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":246,"end":247},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":11,"start":247,"end":248},{"type":"T_DIV","context":"jsxTag","value":"/","line":11,"start":248,"end":249},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"div","line":11,"start":249,"end":252},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":11,"start":252,"end":253},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":253,"end":254},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":257,"end":258},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":259,"end":260}],"errors":[]}
Now, let’s Flowify the component we just wrote:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import * as React from 'react';
type Props = {
foo: number,
bar?: string,
};
class MyComponent extends React.Component<Props> {
render() {
this.props.doesNotExist;
return <div>{this.props.bar}</div>;
}
}
<MyComponent foo={42} />;
|
{"value":"import * as React from 'react';\n\ntype Props = {\n foo: number,\n bar?: string,\n};\n\nclass MyComponent extends React.Component<Props> {\n render() {\n this.props.doesNotExist; // Error! You did not define a `doesNotExist` prop.\n\n return <div>{this.props.bar}</div>;\n }\n}\n\n<MyComponent foo={42} />;\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_MULT","context":"normal","value":"*","line":1,"start":7,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"as","line":1,"start":9,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":1,"start":12,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":18,"end":22},{"type":"T_STRING","context":"normal","value":"'react'","line":1,"start":23,"end":30},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":30,"end":31},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":33,"end":37},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":3,"start":38,"end":43},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":44,"end":45},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":50,"end":53},{"type":"T_COLON","context":"type","value":":","line":4,"start":53,"end":54},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":55,"end":61},{"type":"T_COMMA","context":"type","value":",","line":4,"start":61,"end":62},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":5,"start":65,"end":68},{"type":"T_PLING","context":"type","value":"?","line":5,"start":68,"end":69},{"type":"T_COLON","context":"type","value":":","line":5,"start":69,"end":70},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":71,"end":77},{"type":"T_COMMA","context":"type","value":",","line":5,"start":77,"end":78},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":79,"end":80},{"type":"T_SEMICOLON","context":"type","value":";","line":6,"start":80,"end":81},{"type":"T_CLASS","context":"normal","value":"class","line":8,"start":83,"end":88},{"type":"T_IDENTIFIER","context":"normal","value":"MyComponent","line":8,"start":89,"end":100},{"type":"T_EXTENDS","context":"normal","value":"extends","line":8,"start":101,"end":108},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":8,"start":109,"end":114},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":114,"end":115},{"type":"T_IDENTIFIER","context":"normal","value":"Component","line":8,"start":115,"end":124},{"type":"T_LESS_THAN","context":"type","value":"<","line":8,"start":124,"end":125},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":8,"start":125,"end":130},{"type":"T_GREATER_THAN","context":"type","value":">","line":8,"start":130,"end":131},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":132,"end":133},{"type":"T_IDENTIFIER","context":"normal","value":"render","line":9,"start":136,"end":142},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":142,"end":143},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":143,"end":144},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":145,"end":146},{"type":"T_THIS","context":"normal","value":"this","line":10,"start":151,"end":155},{"type":"T_PERIOD","context":"normal","value":".","line":10,"start":155,"end":156},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":10,"start":156,"end":161},{"type":"T_PERIOD","context":"normal","value":".","line":10,"start":161,"end":162},{"type":"T_IDENTIFIER","context":"normal","value":"doesNotExist","line":10,"start":162,"end":174},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":174,"end":175},{"type":"Line","context":"comment","value":"// Error! You did not define a `doesNotExist` prop.","line":10,"start":176,"end":227},{"type":"T_RETURN","context":"normal","value":"return","line":12,"start":233,"end":239},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":12,"start":240,"end":241},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"div","line":12,"start":241,"end":244},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":12,"start":244,"end":245},{"type":"T_LCURLY","context":"normal","value":"{","line":12,"start":245,"end":246},{"type":"T_THIS","context":"normal","value":"this","line":12,"start":246,"end":250},{"type":"T_PERIOD","context":"normal","value":".","line":12,"start":250,"end":251},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":12,"start":251,"end":256},{"type":"T_PERIOD","context":"normal","value":".","line":12,"start":256,"end":257},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":12,"start":257,"end":260},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":260,"end":261},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":12,"start":261,"end":262},{"type":"T_DIV","context":"jsxTag","value":"/","line":12,"start":262,"end":263},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"div","line":12,"start":263,"end":266},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":12,"start":266,"end":267},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":267,"end":268},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":271,"end":272},{"type":"T_RCURLY","context":"normal","value":"}","line":14,"start":273,"end":274},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":16,"start":276,"end":277},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"MyComponent","line":16,"start":277,"end":288},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"foo","line":16,"start":289,"end":292},{"type":"T_ASSIGN","context":"jsxTag","value":"=","line":16,"start":292,"end":293},{"type":"T_LCURLY","context":"normal","value":"{","line":16,"start":293,"end":294},{"type":"T_NUMBER","context":"normal","value":"42","line":16,"start":294,"end":296},{"type":"T_RCURLY","context":"normal","value":"}","line":16,"start":296,"end":297},{"type":"T_DIV","context":"jsxTag","value":"/","line":16,"start":298,"end":299},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":16,"start":299,"end":300},{"type":"T_SEMICOLON","context":"normal","value":";","line":16,"start":300,"end":301}],"errors":[]}
We removed our dependency on prop-types
and added a Flow object type named
Props
with the same shape as the prop types but using Flow’s static type
syntax. Then we passed our new Props
type into React.Component
as a type
argument.
Now if you try to use <MyComponent>
with a string for foo
instead of a
number you will get an error.
Now wherever we use this.props
in our React component Flow will treat it as
the Props
type we defined.
Note: If you don’t need to use the Props
type again you could also
define it inline: extends React.Component<{ foo: number, bar?: string }>
.
Note: We import React
as a namespace here with
import * as React from 'react'
instead of as a default with
import React from 'react'
. When importing React as an ES module you may use
either style, but importing as a namespace gives you access to React’s
utility types.
React.Component<Props, State>
is a generic type that takes two type
arguments. Props and state. The second type argument, State
, is optional. By
default it is undefined so you can see in the example above we did not include
State
. We will learn more about state in the next section…
Adding State
To add a type for state to your React class component then create a new object
type, in the example below we name it State
, and pass it as the second type
argument to React.Component
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
import * as React from 'react';
type Props = { };
type State = {
count: number,
};
class MyComponent extends React.Component<Props, State> {
state = {
count: 0,
};
componentDidMount() {
setInterval(() => {
this.setState(prevState => ({
count: prevState.count + 1,
}));
}, 1000);
}
render() {
return <div>Count: {this.state.count}</div>;
}
}
<MyComponent />;
|
{"value":"import * as React from 'react';\n\ntype Props = { /* ... */ };\n\ntype State = {\n count: number,\n};\n\nclass MyComponent extends React.Component<Props, State> {\n state = {\n count: 0,\n };\n\n componentDidMount() {\n setInterval(() => {\n this.setState(prevState => ({\n count: prevState.count + 1,\n }));\n }, 1000);\n }\n\n render() {\n return <div>Count: {this.state.count}</div>;\n }\n}\n\n<MyComponent />;\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_MULT","context":"normal","value":"*","line":1,"start":7,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"as","line":1,"start":9,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":1,"start":12,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":18,"end":22},{"type":"T_STRING","context":"normal","value":"'react'","line":1,"start":23,"end":30},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":30,"end":31},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":33,"end":37},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":3,"start":38,"end":43},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":44,"end":45},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":46,"end":47},{"type":"Block","context":"comment","value":"/* ... */","line":3,"start":48,"end":57},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":58,"end":59},{"type":"T_SEMICOLON","context":"type","value":";","line":3,"start":59,"end":60},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":62,"end":66},{"type":"T_IDENTIFIER","context":"type","value":"State","line":5,"start":67,"end":72},{"type":"T_ASSIGN","context":"type","value":"=","line":5,"start":73,"end":74},{"type":"T_LCURLY","context":"type","value":"{","line":5,"start":75,"end":76},{"type":"T_IDENTIFIER","context":"normal","value":"count","line":6,"start":79,"end":84},{"type":"T_COLON","context":"type","value":":","line":6,"start":84,"end":85},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":86,"end":92},{"type":"T_COMMA","context":"type","value":",","line":6,"start":92,"end":93},{"type":"T_RCURLY","context":"type","value":"}","line":7,"start":94,"end":95},{"type":"T_SEMICOLON","context":"type","value":";","line":7,"start":95,"end":96},{"type":"T_CLASS","context":"normal","value":"class","line":9,"start":98,"end":103},{"type":"T_IDENTIFIER","context":"normal","value":"MyComponent","line":9,"start":104,"end":115},{"type":"T_EXTENDS","context":"normal","value":"extends","line":9,"start":116,"end":123},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":9,"start":124,"end":129},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":129,"end":130},{"type":"T_IDENTIFIER","context":"normal","value":"Component","line":9,"start":130,"end":139},{"type":"T_LESS_THAN","context":"type","value":"<","line":9,"start":139,"end":140},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":9,"start":140,"end":145},{"type":"T_COMMA","context":"type","value":",","line":9,"start":145,"end":146},{"type":"T_IDENTIFIER","context":"type","value":"State","line":9,"start":147,"end":152},{"type":"T_GREATER_THAN","context":"type","value":">","line":9,"start":152,"end":153},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":154,"end":155},{"type":"T_IDENTIFIER","context":"normal","value":"state","line":10,"start":158,"end":163},{"type":"T_ASSIGN","context":"normal","value":"=","line":10,"start":164,"end":165},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":166,"end":167},{"type":"T_IDENTIFIER","context":"normal","value":"count","line":11,"start":172,"end":177},{"type":"T_COLON","context":"normal","value":":","line":11,"start":177,"end":178},{"type":"T_NUMBER","context":"normal","value":"0","line":11,"start":179,"end":180},{"type":"T_COMMA","context":"normal","value":",","line":11,"start":180,"end":181},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":184,"end":185},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":185,"end":186},{"type":"T_IDENTIFIER","context":"normal","value":"componentDidMount","line":14,"start":190,"end":207},{"type":"T_LPAREN","context":"normal","value":"(","line":14,"start":207,"end":208},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":208,"end":209},{"type":"T_LCURLY","context":"normal","value":"{","line":14,"start":210,"end":211},{"type":"T_IDENTIFIER","context":"normal","value":"setInterval","line":15,"start":216,"end":227},{"type":"T_LPAREN","context":"normal","value":"(","line":15,"start":227,"end":228},{"type":"T_LPAREN","context":"normal","value":"(","line":15,"start":228,"end":229},{"type":"T_RPAREN","context":"normal","value":")","line":15,"start":229,"end":230},{"type":"T_ARROW","context":"normal","value":"=>","line":15,"start":231,"end":233},{"type":"T_LCURLY","context":"normal","value":"{","line":15,"start":234,"end":235},{"type":"T_THIS","context":"normal","value":"this","line":16,"start":242,"end":246},{"type":"T_PERIOD","context":"normal","value":".","line":16,"start":246,"end":247},{"type":"T_IDENTIFIER","context":"normal","value":"setState","line":16,"start":247,"end":255},{"type":"T_LPAREN","context":"normal","value":"(","line":16,"start":255,"end":256},{"type":"T_IDENTIFIER","context":"normal","value":"prevState","line":16,"start":256,"end":265},{"type":"T_ARROW","context":"normal","value":"=>","line":16,"start":266,"end":268},{"type":"T_LPAREN","context":"normal","value":"(","line":16,"start":269,"end":270},{"type":"T_LCURLY","context":"normal","value":"{","line":16,"start":270,"end":271},{"type":"T_IDENTIFIER","context":"normal","value":"count","line":17,"start":280,"end":285},{"type":"T_COLON","context":"normal","value":":","line":17,"start":285,"end":286},{"type":"T_IDENTIFIER","context":"normal","value":"prevState","line":17,"start":287,"end":296},{"type":"T_PERIOD","context":"normal","value":".","line":17,"start":296,"end":297},{"type":"T_IDENTIFIER","context":"normal","value":"count","line":17,"start":297,"end":302},{"type":"T_PLUS","context":"normal","value":"+","line":17,"start":303,"end":304},{"type":"T_NUMBER","context":"normal","value":"1","line":17,"start":305,"end":306},{"type":"T_COMMA","context":"normal","value":",","line":17,"start":306,"end":307},{"type":"T_RCURLY","context":"normal","value":"}","line":18,"start":314,"end":315},{"type":"T_RPAREN","context":"normal","value":")","line":18,"start":315,"end":316},{"type":"T_RPAREN","context":"normal","value":")","line":18,"start":316,"end":317},{"type":"T_SEMICOLON","context":"normal","value":";","line":18,"start":317,"end":318},{"type":"T_RCURLY","context":"normal","value":"}","line":19,"start":323,"end":324},{"type":"T_COMMA","context":"normal","value":",","line":19,"start":324,"end":325},{"type":"T_NUMBER","context":"normal","value":"1000","line":19,"start":326,"end":330},{"type":"T_RPAREN","context":"normal","value":")","line":19,"start":330,"end":331},{"type":"T_SEMICOLON","context":"normal","value":";","line":19,"start":331,"end":332},{"type":"T_RCURLY","context":"normal","value":"}","line":20,"start":335,"end":336},{"type":"T_IDENTIFIER","context":"normal","value":"render","line":22,"start":340,"end":346},{"type":"T_LPAREN","context":"normal","value":"(","line":22,"start":346,"end":347},{"type":"T_RPAREN","context":"normal","value":")","line":22,"start":347,"end":348},{"type":"T_LCURLY","context":"normal","value":"{","line":22,"start":349,"end":350},{"type":"T_RETURN","context":"normal","value":"return","line":23,"start":355,"end":361},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":23,"start":362,"end":363},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"div","line":23,"start":363,"end":366},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":23,"start":366,"end":367},{"type":"T_JSX_TEXT","context":"jsxChild","value":"Count: ","line":23,"start":367,"end":374},{"type":"T_LCURLY","context":"normal","value":"{","line":23,"start":374,"end":375},{"type":"T_THIS","context":"normal","value":"this","line":23,"start":375,"end":379},{"type":"T_PERIOD","context":"normal","value":".","line":23,"start":379,"end":380},{"type":"T_IDENTIFIER","context":"normal","value":"state","line":23,"start":380,"end":385},{"type":"T_PERIOD","context":"normal","value":".","line":23,"start":385,"end":386},{"type":"T_IDENTIFIER","context":"normal","value":"count","line":23,"start":386,"end":391},{"type":"T_RCURLY","context":"normal","value":"}","line":23,"start":391,"end":392},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":23,"start":392,"end":393},{"type":"T_DIV","context":"jsxTag","value":"/","line":23,"start":393,"end":394},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"div","line":23,"start":394,"end":397},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":23,"start":397,"end":398},{"type":"T_SEMICOLON","context":"normal","value":";","line":23,"start":398,"end":399},{"type":"T_RCURLY","context":"normal","value":"}","line":24,"start":402,"end":403},{"type":"T_RCURLY","context":"normal","value":"}","line":25,"start":404,"end":405},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":27,"start":407,"end":408},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"MyComponent","line":27,"start":408,"end":419},{"type":"T_DIV","context":"jsxTag","value":"/","line":27,"start":420,"end":421},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":27,"start":421,"end":422},{"type":"T_SEMICOLON","context":"normal","value":";","line":27,"start":422,"end":423}],"errors":[]}
In the example above we are using a React setState()
updater function but
you could also pass a partial state object to setState()
.
Note: If you don’t need to use the State
type again you could also
define it inline: extends React.Component<{}, { count: number }>
.
Using Default Props
React supports the notion of defaultProps
which you can think of as default
function arguments. When you create an element and you did not include a prop
with a default then React will substitute that prop with its corresponding
value from defaultProps
. Flow supports this notion as well. To type default
props add a static defaultProps
property to your class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import * as React from 'react';
type Props = {
foo: number,
bar: string,
};
class MyComponent extends React.Component<Props> {
static defaultProps = {
foo: 42,
};
}
<MyComponent bar={"abc"} />
|
{"value":"import * as React from 'react';\n\ntype Props = {\n foo: number, // foo is required.\n bar: string, // bar is required.\n};\n\nclass MyComponent extends React.Component<Props> {\n static defaultProps = {\n foo: 42, // ...but we have a default prop for foo.\n };\n}\n\n// So we don't need to include foo.\n<MyComponent bar={\"abc\"} />\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_MULT","context":"normal","value":"*","line":1,"start":7,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"as","line":1,"start":9,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":1,"start":12,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":18,"end":22},{"type":"T_STRING","context":"normal","value":"'react'","line":1,"start":23,"end":30},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":30,"end":31},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":33,"end":37},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":3,"start":38,"end":43},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":44,"end":45},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":50,"end":53},{"type":"T_COLON","context":"type","value":":","line":4,"start":53,"end":54},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":55,"end":61},{"type":"T_COMMA","context":"type","value":",","line":4,"start":61,"end":62},{"type":"Line","context":"comment","value":"// foo is required.","line":4,"start":63,"end":82},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":5,"start":85,"end":88},{"type":"T_COLON","context":"type","value":":","line":5,"start":88,"end":89},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":90,"end":96},{"type":"T_COMMA","context":"type","value":",","line":5,"start":96,"end":97},{"type":"Line","context":"comment","value":"// bar is required.","line":5,"start":98,"end":117},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":118,"end":119},{"type":"T_SEMICOLON","context":"type","value":";","line":6,"start":119,"end":120},{"type":"T_CLASS","context":"normal","value":"class","line":8,"start":122,"end":127},{"type":"T_IDENTIFIER","context":"normal","value":"MyComponent","line":8,"start":128,"end":139},{"type":"T_EXTENDS","context":"normal","value":"extends","line":8,"start":140,"end":147},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":8,"start":148,"end":153},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":153,"end":154},{"type":"T_IDENTIFIER","context":"normal","value":"Component","line":8,"start":154,"end":163},{"type":"T_LESS_THAN","context":"type","value":"<","line":8,"start":163,"end":164},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":8,"start":164,"end":169},{"type":"T_GREATER_THAN","context":"type","value":">","line":8,"start":169,"end":170},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":171,"end":172},{"type":"T_STATIC","context":"normal","value":"static","line":9,"start":175,"end":181},{"type":"T_IDENTIFIER","context":"normal","value":"defaultProps","line":9,"start":182,"end":194},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":195,"end":196},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":197,"end":198},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":10,"start":203,"end":206},{"type":"T_COLON","context":"normal","value":":","line":10,"start":206,"end":207},{"type":"T_NUMBER","context":"normal","value":"42","line":10,"start":208,"end":210},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":210,"end":211},{"type":"Line","context":"comment","value":"// ...but we have a default prop for foo.","line":10,"start":212,"end":253},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":256,"end":257},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":257,"end":258},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":259,"end":260},{"type":"Line","context":"comment","value":"// So we don't need to include foo.","line":14,"start":262,"end":297},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":15,"start":298,"end":299},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"MyComponent","line":15,"start":299,"end":310},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"bar","line":15,"start":311,"end":314},{"type":"T_ASSIGN","context":"jsxTag","value":"=","line":15,"start":314,"end":315},{"type":"T_LCURLY","context":"normal","value":"{","line":15,"start":315,"end":316},{"type":"T_STRING","context":"normal","value":"\"abc\"","line":15,"start":316,"end":321},{"type":"T_RCURLY","context":"normal","value":"}","line":15,"start":321,"end":322},{"type":"T_DIV","context":"jsxTag","value":"/","line":15,"start":323,"end":324},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":15,"start":324,"end":325}],"errors":[]}
Flow will infer the type of your default props from static defaultProps
so you
don’t have to add any type annotations to use default props.
Note: You don’t need to make foo
nullable in your Props
type. Flow
will make sure that foo
is optional if you have a default prop for foo
.
If you would like to add a type annotation to defaultProps
you can define the
type as
1
2
3
|
type DefaultProps = {|
foo: number,
|}
|
{"value":"type DefaultProps = {|\n foo: number,\n|}\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"DefaultProps","line":1,"start":5,"end":17},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":18,"end":19},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":1,"start":20,"end":22},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":25,"end":28},{"type":"T_COLON","context":"type","value":":","line":2,"start":28,"end":29},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":30,"end":36},{"type":"T_COMMA","context":"type","value":",","line":2,"start":36,"end":37},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":3,"start":38,"end":40}],"errors":[]}
and spread that into the Props
type:
1
2
3
4
|
type Props = {
...DefaultProps,
bar: string,
}
|
{"value":"type Props = {\n ...DefaultProps,\n bar: string,\n}\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":1,"start":5,"end":10},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":11,"end":12},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":13,"end":14},{"type":"T_ELLIPSIS","context":"type","value":"...","line":2,"start":17,"end":20},{"type":"T_IDENTIFIER","context":"type","value":"DefaultProps","line":2,"start":20,"end":32},{"type":"T_COMMA","context":"type","value":",","line":2,"start":32,"end":33},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":3,"start":36,"end":39},{"type":"T_COLON","context":"type","value":":","line":3,"start":39,"end":40},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":41,"end":47},{"type":"T_COMMA","context":"type","value":",","line":3,"start":47,"end":48},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":49,"end":50}],"errors":[]}
This way you avoid duplicating the properties that happen to have a default value.
Stateless Functional Components
In addition to classes, React also supports stateless functional components.
You type these components like you would type a function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import * as React from 'react';
type Props = {
foo: number,
bar?: string,
};
function MyComponent(props: Props) {
props.doesNotExist;
return <div>{props.bar}</div>;
}
<MyComponent foo={42} />
|
{"value":"import * as React from 'react';\n\ntype Props = {\n foo: number,\n bar?: string,\n};\n\nfunction MyComponent(props: Props) {\n props.doesNotExist; // Error! You did not define a `doesNotExist` prop.\n\n return <div>{props.bar}</div>;\n}\n\n<MyComponent foo={42} />\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_MULT","context":"normal","value":"*","line":1,"start":7,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"as","line":1,"start":9,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":1,"start":12,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":18,"end":22},{"type":"T_STRING","context":"normal","value":"'react'","line":1,"start":23,"end":30},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":30,"end":31},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":33,"end":37},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":3,"start":38,"end":43},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":44,"end":45},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":50,"end":53},{"type":"T_COLON","context":"type","value":":","line":4,"start":53,"end":54},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":55,"end":61},{"type":"T_COMMA","context":"type","value":",","line":4,"start":61,"end":62},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":5,"start":65,"end":68},{"type":"T_PLING","context":"type","value":"?","line":5,"start":68,"end":69},{"type":"T_COLON","context":"type","value":":","line":5,"start":69,"end":70},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":71,"end":77},{"type":"T_COMMA","context":"type","value":",","line":5,"start":77,"end":78},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":79,"end":80},{"type":"T_SEMICOLON","context":"type","value":";","line":6,"start":80,"end":81},{"type":"T_FUNCTION","context":"normal","value":"function","line":8,"start":83,"end":91},{"type":"T_IDENTIFIER","context":"normal","value":"MyComponent","line":8,"start":92,"end":103},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":103,"end":104},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":8,"start":104,"end":109},{"type":"T_COLON","context":"type","value":":","line":8,"start":109,"end":110},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":8,"start":111,"end":116},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":116,"end":117},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":118,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":9,"start":122,"end":127},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":127,"end":128},{"type":"T_IDENTIFIER","context":"normal","value":"doesNotExist","line":9,"start":128,"end":140},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":140,"end":141},{"type":"Line","context":"comment","value":"// Error! You did not define a `doesNotExist` prop.","line":9,"start":142,"end":193},{"type":"T_RETURN","context":"normal","value":"return","line":11,"start":197,"end":203},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":11,"start":204,"end":205},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"div","line":11,"start":205,"end":208},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":11,"start":208,"end":209},{"type":"T_LCURLY","context":"normal","value":"{","line":11,"start":209,"end":210},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":11,"start":210,"end":215},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":215,"end":216},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":11,"start":216,"end":219},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":219,"end":220},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":11,"start":220,"end":221},{"type":"T_DIV","context":"jsxTag","value":"/","line":11,"start":221,"end":222},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"div","line":11,"start":222,"end":225},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":11,"start":225,"end":226},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":226,"end":227},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":228,"end":229},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":14,"start":231,"end":232},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"MyComponent","line":14,"start":232,"end":243},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"foo","line":14,"start":244,"end":247},{"type":"T_ASSIGN","context":"jsxTag","value":"=","line":14,"start":247,"end":248},{"type":"T_LCURLY","context":"normal","value":"{","line":14,"start":248,"end":249},{"type":"T_NUMBER","context":"normal","value":"42","line":14,"start":249,"end":251},{"type":"T_RCURLY","context":"normal","value":"}","line":14,"start":251,"end":252},{"type":"T_DIV","context":"jsxTag","value":"/","line":14,"start":253,"end":254},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":14,"start":254,"end":255}],"errors":[]}
Using Default Props for Functional Components
React also supports default props on stateless functional components. Similarly
to class components, default props for stateless functional components will
work without any extra type annotations.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import * as React from 'react';
type Props = {
foo: number,
};
function MyComponent(props: Props) {}
MyComponent.defaultProps = {
foo: 42,
};
<MyComponent />;
|
{"value":"import * as React from 'react';\n\ntype Props = {\n foo: number, // foo is required.\n};\n\nfunction MyComponent(props: Props) {}\n\nMyComponent.defaultProps = {\n foo: 42, // ...but we have a default prop for foo.\n};\n\n// So we don't need to include foo.\n<MyComponent />;\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_MULT","context":"normal","value":"*","line":1,"start":7,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"as","line":1,"start":9,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":1,"start":12,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":18,"end":22},{"type":"T_STRING","context":"normal","value":"'react'","line":1,"start":23,"end":30},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":30,"end":31},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":33,"end":37},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":3,"start":38,"end":43},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":44,"end":45},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":50,"end":53},{"type":"T_COLON","context":"type","value":":","line":4,"start":53,"end":54},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":55,"end":61},{"type":"T_COMMA","context":"type","value":",","line":4,"start":61,"end":62},{"type":"Line","context":"comment","value":"// foo is required.","line":4,"start":63,"end":82},{"type":"T_RCURLY","context":"type","value":"}","line":5,"start":83,"end":84},{"type":"T_SEMICOLON","context":"type","value":";","line":5,"start":84,"end":85},{"type":"T_FUNCTION","context":"normal","value":"function","line":7,"start":87,"end":95},{"type":"T_IDENTIFIER","context":"normal","value":"MyComponent","line":7,"start":96,"end":107},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":107,"end":108},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":7,"start":108,"end":113},{"type":"T_COLON","context":"type","value":":","line":7,"start":113,"end":114},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":7,"start":115,"end":120},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":120,"end":121},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":122,"end":123},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":123,"end":124},{"type":"T_IDENTIFIER","context":"normal","value":"MyComponent","line":9,"start":126,"end":137},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":137,"end":138},{"type":"T_IDENTIFIER","context":"normal","value":"defaultProps","line":9,"start":138,"end":150},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":151,"end":152},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":153,"end":154},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":10,"start":157,"end":160},{"type":"T_COLON","context":"normal","value":":","line":10,"start":160,"end":161},{"type":"T_NUMBER","context":"normal","value":"42","line":10,"start":162,"end":164},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":164,"end":165},{"type":"Line","context":"comment","value":"// ...but we have a default prop for foo.","line":10,"start":166,"end":207},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":208,"end":209},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":209,"end":210},{"type":"Line","context":"comment","value":"// So we don't need to include foo.","line":13,"start":212,"end":247},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":14,"start":248,"end":249},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"MyComponent","line":14,"start":249,"end":260},{"type":"T_DIV","context":"jsxTag","value":"/","line":14,"start":261,"end":262},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":14,"start":262,"end":263},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":263,"end":264}],"errors":[]}
Note: You don’t need to make foo
nullable in your Props
type. Flow
will make sure that foo
is optional if you have a default prop for foo
.