Skip to content
This repository has been archived by the owner on Sep 10, 2022. It is now read-only.

Commit

Permalink
Include flow types into library (#649)
Browse files Browse the repository at this point in the history
* Fix types for modern flow

* Move flow declarations and test into recompose

* Fix docs

* Remove errors

* Add yarn-error to gitignore

* Append flow 2 test

* Update release to support flow types

* Remove unneeded command
  • Loading branch information
istarkov committed Apr 19, 2018
1 parent 6d3765a commit d9042e7
Show file tree
Hide file tree
Showing 35 changed files with 430 additions and 455 deletions.
4 changes: 2 additions & 2 deletions types/flow-typed/.flowconfig → .flowconfig
@@ -1,11 +1,11 @@
[ignore]
<PROJECT_ROOT>/types/.*

[include]

[libs]
../flow-typed/recompose_v0.24.x/flow_v0.55.x-/recompose_v0.24.x.js

[options]
suppress_comment=\\(.\\|\n\\)*\\$ExpectError

[lints]
[lints]
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -3,3 +3,4 @@ release
lib
coverage
.vscode
yarn-error.log
2 changes: 2 additions & 0 deletions .prettierignore
@@ -0,0 +1,2 @@
node_modules
package.json
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -17,7 +17,7 @@ Recompose is a React utility belt for function components and higher-order compo
npm install recompose --save
```

**📺 Watch Andrew's [talk on Recompose at React Europe](https://www.youtube.com/watch?v=zD_judE-bXk).**
**📺 Watch Andrew's [talk on Recompose at React Europe](https://www.youtube.com/watch?v=zD_judE-bXk).**
*(Note: Performance optimizations he speaks about have been removed, more info [here](https://github.com/acdlite/recompose/releases/tag/v0.26.0))*

### Related modules
Expand Down Expand Up @@ -159,7 +159,7 @@ const ClassComponent = toClass(FunctionComponent)

## Flow support

[Read the docs](types)
[Read the docs](docs/flow.md)

## Translation

Expand Down
92 changes: 92 additions & 0 deletions docs/flow.md
@@ -0,0 +1,92 @@
# Flow support for recompose

## How it works

In most cases all you need is to declare a props type of enhanced Component.
Flow will infer all other types you need.

Example:

```javascript
import type { HOC } from 'recompose';

type EnhancedComponentProps = {
text?: string,
};

const baseComponent = ({ text }) => <div>{text}</div>;

const enhance:HOC<*, EnhancedComponentProps> = compose(
defaultProps({
text: 'world',
}),
withProps(({ text }) => ({
text: `Hello ${text}`
}))
);

export default enhance(baseComponent);

```

See it in action.

![recompose-flow](https://user-images.githubusercontent.com/5077042/28116959-0c96ae2c-6714-11e7-930e-b1454c629908.gif)

## How to start

The easiest way is to start from example.

Look at [this](http://grader-meets-16837.netlify.com/) app [source](../types/flow-example)


## Support

Type definitions of recompose HOCs are splitted into 2 parts.

### Part 1 - HOCs with good flow support

In most cases you can use them without big issues.
Type inference and errors detection works near well.

These HOCs are: *defaultProps, mapProps, withProps, withStateHandlers, withHandlers, pure, onlyUpdateForKeys, shouldUpdate, renderNothing, renderComponent, branch, withPropsOnChange, onlyUpdateForPropTypes, toClass, withContext, getContext, setStatic, setPropTypes, setDisplayName*

#### Known issues for "good" HOCs

see `test_mapProps.js` - inference work but type errors are not detected in hocs

### Part 2 - other HOCs

To use these HOCs - you need to provide type information (no automatic type inference).
You must be a good voodoo dancer.

See `test_voodoo.js` for the idea.

Some recomendations:

- *flattenProp,renameProp, renameProps* can easily be replaced with _withProps_
- *withReducer, withState* -> use _withStateHandlers_ instead
- _lifecycle_ -> you don't need recompose if you need a _lifecycle_, just use React class instead
- _mapPropsStream_ -> see `test_mapPropsStream.js`

#### Known issues for above HOCs

See `test_voodoo.js`, `test_mapPropsStream.js`

### Utils

*getDisplayName, wrapDisplayName, shallowEqual,isClassComponent, createSink, componentFromProp, nest, hoistStatics.*

### Articles

[Typing Higher-order Components in Recompose With Flow](https://medium.com/flow-type/flow-support-in-recompose-1b76f58f4cfc)

### Faq

Why to use existential type with `HOC<*, Blbla>` isn't it possible to avoid this?

*I tried to use type alias but haven't found how to make it work.*

## Thanks

Big thanks to [@gcanti](https://github.com/gcanti) for his work on PR [#241](https://github.com/acdlite/recompose/pull/241), it was nice and clear base for current definitions.
5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -9,8 +9,8 @@
"license": "MIT",
"scripts": {
"lint": "eslint scripts src",
"build-recompose": "cross-env PACKAGE_NAME=recompose rollup --config scripts/rollup.config.js",
"test": "jest",
"build:recompose": "cross-env PACKAGE_NAME=recompose rollup --config scripts/rollup.config.js",
"test": "jest && flow",
"test:watch": "cross-env BABEL_ENV=cjs jest --watch",
"release": "node scripts/release.js",
"postinstall": "node scripts/installNestedPackageDeps.js",
Expand Down Expand Up @@ -60,6 +60,7 @@
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-prettier": "^2.0.1",
"eslint-plugin-react": "^6.10.3",
"flow-bin": "^0.70.0",
"flyd": "^0.2.4",
"husky": "^0.13.3",
"jest": "^22.4.3",
Expand Down
14 changes: 11 additions & 3 deletions scripts/release.js
Expand Up @@ -8,6 +8,7 @@ const { flowRight: compose } = require('lodash')
const readline = require('readline-sync')
const semver = require('semver')
const glob = require('glob')
const { pascalCase } = require('change-case')

const BIN = './node_modules/.bin'

Expand Down Expand Up @@ -47,6 +48,8 @@ try {
)
}

const libraryName = pascalCase(packageName)

const versionLoc = path.resolve(PACKAGES_SRC_DIR, packageName, 'VERSION')
const version = fs.readFileSync(versionLoc, 'utf8').trim()

Expand Down Expand Up @@ -119,13 +122,18 @@ try {
)

log(`Building ${packageName}...`)
const runRollup = () =>
`cross-env PACKAGE_NAME:${packageName}` +
' rollup --config scripts/rollup.config.js'
const runRollup = () => `yarn build:${packageName}`
if (exec(runRollup()).code !== 0) {
exit(1)
}

log(`Preparing ${libraryName}.cjs.js.flow...`)
cp(
'-f',
path.resolve(sourceDir, 'index.js.flow'),
path.resolve(outDir, 'dist', `${libraryName}.cjs.js.flow`)
)

log(`About to publish ${packageName}@${nextVersion} to npm.`)
if (!readline.keyInYN('Sound good? ')) {
log('OK. Stopping release.')
Expand Down
Expand Up @@ -8,9 +8,9 @@ import {
renderNothing,
renderComponent,
onlyUpdateForKeys,
} from 'recompose'
} from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down
@@ -1,7 +1,7 @@
/* @flow */
import * as React from 'react'
import { compose, withProps } from 'recompose'
import type { HOC } from 'recompose'
import { compose, withProps } from '../..'
import type { HOC } from '../..'

// Example of very dirty written fetcher enhancer
function fetcher<Response: {}, Base: {}>(
Expand Down
@@ -1,6 +1,6 @@
// @flow
import React from 'react'
import { componentFromStream } from 'recompose'
import { componentFromStream } from '../..'

// $ExpectError
componentFromStream(1)
Expand Down
@@ -1,6 +1,6 @@
// @flow
import React from 'react'
import { createEventHandler } from 'recompose'
import { createEventHandler } from '../..'

// $ExpectError
createEventHandler(1)
Expand Down
@@ -1,9 +1,9 @@
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, defaultProps } from 'recompose'
import { compose, withProps, defaultProps } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down
@@ -1,8 +1,8 @@
/* @flow */

import * as React from 'react'
import { compose, withProps } from 'recompose'
import type { HOC } from 'recompose'
import { compose, withProps } from '../..'
import type { HOC } from '../..'

function mapProps<BaseProps: {}, EnhancedProps>(
mapperFn: EnhancedProps => BaseProps
Expand Down
@@ -1,9 +1,9 @@
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, withProps, getContext } from 'recompose'
import { compose, withProps, getContext } from '../..'
// import PropTypes from 'prop-types'
import type { HOC } from 'recompose'
import type { HOC } from '../..'

const PropTypes = {
number: () => {},
Expand Down
Expand Up @@ -2,9 +2,9 @@
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, mapProps, withProps } from 'recompose'
import { compose, mapProps, withProps } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down
Expand Up @@ -3,9 +3,9 @@
/* @flow */
import React from 'react'
// import { Observable } from 'rxjs'
import { compose, mapProps, withProps, mapPropsStream } from 'recompose'
import { compose, mapProps, withProps, mapPropsStream } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down
@@ -1,9 +1,9 @@
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, onlyUpdateForKeys } from 'recompose'
import { compose, withProps, onlyUpdateForKeys } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down
@@ -1,9 +1,9 @@
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, onlyUpdateForPropTypes } from 'recompose'
import { compose, withProps, onlyUpdateForPropTypes } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down
@@ -1,9 +1,9 @@
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, pure } from 'recompose'
import { compose, withProps, pure } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down
@@ -1,9 +1,9 @@
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, withProps, shouldUpdate } from 'recompose'
import { compose, withProps, shouldUpdate } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down
Expand Up @@ -7,9 +7,9 @@ import {
setStatic,
setPropTypes,
setDisplayName,
} from 'recompose'
} from '../..'
// import PropTypes from 'prop-types'
import type { HOC } from 'recompose'
import type { HOC } from '../..'

const PropTypes = {
string: () => {},
Expand Down
@@ -1,9 +1,9 @@
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, toClass } from 'recompose'
import { compose, withProps, toClass } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down
Expand Up @@ -2,9 +2,9 @@
/* @flow */

import React from 'react'
import { compose, withProps, hoistStatics } from 'recompose'
import { compose, withProps, hoistStatics } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { a: number }

Expand Down
Expand Up @@ -9,9 +9,9 @@ import {
renameProp,
renameProps,
withState,
} from 'recompose'
} from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = {
eA: number,
Expand Down
@@ -1,9 +1,9 @@
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, withProps, withContext } from 'recompose'
import { compose, withProps, withContext } from '../..'

import type { HOC } from 'recompose'
import type { HOC } from '../..'

type EnhancedCompProps = { eA: 1 }

Expand Down

0 comments on commit d9042e7

Please sign in to comment.