Showing preview only (248K chars total). Download the full file or copy to clipboard to get everything.
Repository: acdlite/recompose
Branch: master
Commit: 3db12ce7121a
Files: 170
Total size: 211.5 KB
Directory structure:
gitextract_7mr7594c/
├── .codeclimate.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .flowconfig
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc
├── .size-snapshot.json
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── babel.config.js
├── docs/
│ ├── API.md
│ ├── flow.md
│ └── performance.md
├── package.json
├── scripts/
│ ├── getPackageNames.js
│ ├── installNestedPackageDeps.js
│ ├── jest.setup.js
│ ├── release.js
│ └── rollup.config.js
├── src/
│ ├── basePackage.json
│ └── packages/
│ ├── recompose/
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── VERSION
│ │ ├── __tests__/
│ │ │ ├── branch-test.js
│ │ │ ├── componentFromProp-test.js
│ │ │ ├── componentFromStream-test.js
│ │ │ ├── componentFromStreamWithConfig-test.js
│ │ │ ├── compose-test.js
│ │ │ ├── createEventHandler-test.js
│ │ │ ├── createSink-test.js
│ │ │ ├── defaultProps-test.js
│ │ │ ├── fixtures/
│ │ │ │ └── treeshake-entry.js
│ │ │ ├── flattenProp-test.js
│ │ │ ├── fromRenderProps-test.js
│ │ │ ├── getContext-test.js
│ │ │ ├── getDisplayName-test.js
│ │ │ ├── hoistStatics-test.js
│ │ │ ├── isClassComponent-test.js
│ │ │ ├── lifecycle-test.js
│ │ │ ├── mapProps-test.js
│ │ │ ├── mapPropsStream-test.js
│ │ │ ├── mapPropsStreamWithConfig-test.js
│ │ │ ├── nest-test.js
│ │ │ ├── onlyUpdateForKeys-test.js
│ │ │ ├── onlyUpdateForPropTypes-test.js
│ │ │ ├── pure-test.js
│ │ │ ├── renameProp-test.js
│ │ │ ├── renameProps-test.js
│ │ │ ├── renderComponent-test.js
│ │ │ ├── renderNothing-test.js
│ │ │ ├── setDisplayName-test.js
│ │ │ ├── setObservableConfig-test.js
│ │ │ ├── setPropTypes-test.js
│ │ │ ├── setStatic-test.js
│ │ │ ├── shallowEqual-test.js
│ │ │ ├── shouldUpdate-test.js
│ │ │ ├── toClass-test.js
│ │ │ ├── toRenderProps-test.js
│ │ │ ├── types/
│ │ │ │ ├── test_branch.js
│ │ │ │ ├── test_classBasedEnhancer.js
│ │ │ │ ├── test_componentFromStream.js
│ │ │ │ ├── test_createEventHandler.js
│ │ │ │ ├── test_defaultProps.js
│ │ │ │ ├── test_fromRenderProps.js
│ │ │ │ ├── test_functionalEnhancer.js
│ │ │ │ ├── test_getContext.js
│ │ │ │ ├── test_mapProps.js
│ │ │ │ ├── test_mapPropsStream.js
│ │ │ │ ├── test_onlyUpdateForKeys.js
│ │ │ │ ├── test_onlyUpdateForPropTypes.js
│ │ │ │ ├── test_pure.js
│ │ │ │ ├── test_shouldUpdate.js
│ │ │ │ ├── test_statics.js
│ │ │ │ ├── test_toClass.js
│ │ │ │ ├── test_toRenderProps.js
│ │ │ │ ├── test_utils.js
│ │ │ │ ├── test_voodoo.js
│ │ │ │ ├── test_withContext.js
│ │ │ │ ├── test_withHandlers.js
│ │ │ │ ├── test_withProps.js
│ │ │ │ ├── test_withPropsOnChange.js
│ │ │ │ └── test_withStateHandlers.js
│ │ │ ├── utils.js
│ │ │ ├── withContext-test.js
│ │ │ ├── withHandlers-test.js
│ │ │ ├── withProps-test.js
│ │ │ ├── withPropsOnChange-test.js
│ │ │ ├── withReducer-test.js
│ │ │ ├── withState-test.js
│ │ │ ├── withStateHandlers-test.js
│ │ │ └── wrapDisplayName-test.js
│ │ ├── baconObservableConfig.js
│ │ ├── branch.js
│ │ ├── componentFromProp.js
│ │ ├── componentFromStream.js
│ │ ├── compose.js
│ │ ├── createEventHandler.js
│ │ ├── createSink.js
│ │ ├── defaultProps.js
│ │ ├── flattenProp.js
│ │ ├── flydObservableConfig.js
│ │ ├── fromRenderProps.js
│ │ ├── getContext.js
│ │ ├── getDisplayName.js
│ │ ├── hoistStatics.js
│ │ ├── index.js
│ │ ├── index.js.flow
│ │ ├── isClassComponent.js
│ │ ├── kefirObservableConfig.js
│ │ ├── lifecycle.js
│ │ ├── mapProps.js
│ │ ├── mapPropsStream.js
│ │ ├── mostObservableConfig.js
│ │ ├── nest.js
│ │ ├── onlyUpdateForKeys.js
│ │ ├── onlyUpdateForPropTypes.js
│ │ ├── package.json
│ │ ├── pure.js
│ │ ├── renameProp.js
│ │ ├── renameProps.js
│ │ ├── renderComponent.js
│ │ ├── renderNothing.js
│ │ ├── rxjs4ObservableConfig.js
│ │ ├── rxjsObservableConfig.js
│ │ ├── setDisplayName.js
│ │ ├── setObservableConfig.js
│ │ ├── setPropTypes.js
│ │ ├── setStatic.js
│ │ ├── shallowEqual.js
│ │ ├── shouldUpdate.js
│ │ ├── toClass.js
│ │ ├── toRenderProps.js
│ │ ├── utils/
│ │ │ ├── mapValues.js
│ │ │ ├── omit.js
│ │ │ └── pick.js
│ │ ├── withContext.js
│ │ ├── withHandlers.js
│ │ ├── withProps.js
│ │ ├── withPropsOnChange.js
│ │ ├── withReducer.js
│ │ ├── withState.js
│ │ ├── withStateHandlers.js
│ │ ├── wrapDisplayName.js
│ │ └── xstreamObservableConfig.js
│ └── recompose-relay/
│ ├── .npmignore
│ ├── README.md
│ ├── VERSION
│ ├── createContainer.js
│ ├── index.js
│ └── package.json
└── types/
├── README.md
└── flow-example/
├── .eslintrc
├── .flowconfig
├── .gitignore
├── README.md
├── flow-typed/
│ ├── glamor.js
│ └── react-motion.js
├── package.json
├── public/
│ ├── index.html
│ └── manifest.json
└── src/
├── App.js
├── Item.js
├── ItemsAnimator.js
├── MouseDetector.js
└── index.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .codeclimate.yml
================================================
---
engines:
duplication:
enabled: true
config:
languages:
- javascript
eslint:
enabled: true
fixme:
enabled: true
ratings:
paths:
- "**.js"
exclude_paths:
- "**/*-test.js"
================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
================================================
FILE: .eslintignore
================================================
**/node_modules
**/types
================================================
FILE: .eslintrc
================================================
{
"extends": [
"eslint-config-airbnb",
"prettier",
"prettier/flowtype",
"prettier/react"
],
"plugins": [
"prettier"
],
"parser": "babel-eslint",
"env": {
"browser": true,
"node": true,
"jest": true
},
"globals": {
"sinon": true
},
"rules": {
"semi": [2, "never"],
"no-use-before-define": ["error", { "functions": false }],
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"no-underscore-dangle": [0],
"no-confusing-arrow": [0],
"no-class-assign": [0],
"no-plusplus": [0],
"no-prototype-builtins": [0],
"no-return-assign": [0],
"max-len": ["error", { "code": 120, "ignorePattern": "^test", "ignoreUrls": true }],
"lines-between-class-members": [0],
"prefer-destructuring": [0],
"import/no-unresolved": [0],
"import/no-extraneous-dependencies": [0],
"import/extensions": [0],
"import/prefer-default-export": [0],
"import/no-useless-path-segments": [0],
"jsx-a11y/label-has-for": [0],
"react/forbid-prop-types": [0],
"react/prop-types": [0],
"react/prefer-stateless-function": [0],
"react/no-multi-comp": [0],
"react/sort-comp": [0],
"react/jsx-filename-extension": [0],
"prettier/prettier": ["error", {"semi": false, "trailingComma": "es5", "singleQuote": true}],
"react/destructuring-assignment": [0],
"react/jsx-curly-brace-presence": [0],
"react/no-unused-prop-types": [0],
"react/require-default-props": [0],
"react/button-has-type": [0]
}
}
================================================
FILE: .flowconfig
================================================
[ignore]
<PROJECT_ROOT>/types/.*
[include]
[libs]
[options]
suppress_comment=\\(.\\|\n\\)*\\$ExpectError
[lints]
================================================
FILE: .gitignore
================================================
node_modules
release
lib
coverage
.vscode
yarn-error.log
================================================
FILE: .npmignore
================================================
/**/__tests__
coverage
types
================================================
FILE: .prettierignore
================================================
node_modules
package.json
================================================
FILE: .prettierrc
================================================
semi: false
singleQuote: true
trailingComma: es5
================================================
FILE: .size-snapshot.json
================================================
{
"lib/packages/recompose/dist/Recompose.umd.js": {
"bundled": 46425,
"minified": 16484,
"gzipped": 4625
},
"lib/packages/recompose/dist/Recompose.min.js": {
"bundled": 42863,
"minified": 15204,
"gzipped": 4194
},
"lib/packages/recompose/dist/Recompose.esm.js": {
"bundled": 32428,
"minified": 15083,
"gzipped": 3550,
"treeshaked": {
"rollup": {
"code": 310,
"import_statements": 310
},
"webpack": {
"code": 1838
}
}
}
}
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- "node"
install: "yarn install --ignore-engines"
before_script: "npm install -g codeclimate-test-reporter"
script:
- yarn run lint
- yarn test
after_script:
- "CODECLIMATE_REPO_TOKEN=27125df6192d300baf67cd5f5eab6597c998256f4883b744a1055d0f0c18e608 codeclimate-test-reporter < coverage/lcov.info"
- "cat ./coverage/lcov.info | $(npm bin)/codecov"
================================================
FILE: CHANGELOG.md
================================================
We are using the [Github Releases page](https://github.com/acdlite/recompose/releases) as our CHANGELOG.
================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
Copyright (c) 2015-2018 Andrew Clark
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
## A Note from the Author (acdlite, Oct 25 2018):
Hi! I created Recompose about three years ago. About a year after that, I joined the React team. Today, we announced a proposal for [*Hooks*](https://reactjs.org/hooks). Hooks solves all the problems I attempted to address with Recompose three years ago, and more on top of that. I will be discontinuing active maintenance of this package (excluding perhaps bugfixes or patches for compatibility with future React releases), and recommending that people use Hooks instead. **Your existing code with Recompose will still work**, just don't expect any new features. Thank you so, so much to [@wuct](https://github.com/wuct) and [@istarkov](https://github.com/istarkov) for their heroic work maintaining Recompose over the last few years.
Read more discussion about this decision [here](https://github.com/acdlite/recompose/issues/756#issuecomment-438674573).
***
Recompose
-----
[](https://travis-ci.org/acdlite/recompose)
[](https://codecov.io/github/acdlite/recompose)
[](https://codeclimate.com/github/acdlite/recompose)
[](https://www.npmjs.com/package/recompose)
[](https://www.npmjs.com/package/recompose)
Recompose is a React utility belt for function components and higher-order components. Think of it like lodash for React.
[**Full API documentation**](docs/API.md) - Learn about each helper
[**Recompose Base Fiddle**](https://jsfiddle.net/evenchange4/p3vsmrvo/1599/) - Easy way to dive in
```
npm install recompose --save
```
**📺 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
[**recompose-relay**](src/packages/recompose-relay) — Recompose helpers for Relay
## You can use Recompose to...
### ...lift state into functional wrappers
Helpers like `withState()` and `withReducer()` provide a nicer way to express state updates:
```js
const enhance = withState('counter', 'setCounter', 0)
const Counter = enhance(({ counter, setCounter }) =>
<div>
Count: {counter}
<button onClick={() => setCounter(n => n + 1)}>Increment</button>
<button onClick={() => setCounter(n => n - 1)}>Decrement</button>
</div>
)
```
Or with a Redux-style reducer:
```js
const counterReducer = (count, action) => {
switch (action.type) {
case INCREMENT:
return count + 1
case DECREMENT:
return count - 1
default:
return count
}
}
const enhance = withReducer('counter', 'dispatch', counterReducer, 0)
const Counter = enhance(({ counter, dispatch }) =>
<div>
Count: {counter}
<button onClick={() => dispatch({ type: INCREMENT })}>Increment</button>
<button onClick={() => dispatch({ type: DECREMENT })}>Decrement</button>
</div>
)
```
### ...perform the most common React patterns
Helpers like `componentFromProp()` and `withContext()` encapsulate common React patterns into a simple functional interface:
```js
const enhance = defaultProps({ component: 'button' })
const Button = enhance(componentFromProp('component'))
<Button /> // renders <button>
<Button component={Link} /> // renders <Link />
```
```js
const provide = store => withContext(
{ store: PropTypes.object },
() => ({ store })
)
// Apply to base component
// Descendants of App have access to context.store
const AppWithContext = provide(store)(App)
```
### ...optimize rendering performance
No need to write a new class just to implement `shouldComponentUpdate()`. Recompose helpers like `pure()` and `onlyUpdateForKeys()` do this for you:
```js
// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}
// Optimized version of same component, using shallow comparison of props
// Same effect as extending React.PureComponent
const OptimizedComponent = pure(ExpensiveComponent)
// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)
```
### ...interoperate with other libraries
Recompose helpers integrate really nicely with external libraries like Relay, Redux, and RxJS
```js
const enhance = compose(
// This is a Recompose-friendly version of Relay.createContainer(), provided by recompose-relay
createContainer({
fragments: {
post: () => Relay.QL`
fragment on Post {
title,
content
}
`
}
}),
flattenProp('post')
)
const Post = enhance(({ title, content }) =>
<article>
<h1>{title}</h1>
<div>{content}</div>
</article>
)
```
### ...build your own libraries
Many React libraries end up implementing the same utilities over and over again, like `shallowEqual()` and `getDisplayName()`. Recompose provides these utilities for you.
```js
// Any Recompose module can be imported individually
import getDisplayName from 'recompose/getDisplayName'
ConnectedComponent.displayName = `connect(${getDisplayName(BaseComponent)})`
// Or, even better:
import wrapDisplayName from 'recompose/wrapDisplayName'
ConnectedComponent.displayName = wrapDisplayName(BaseComponent, 'connect')
import toClass from 'recompose/toClass'
// Converts a function component to a class component, e.g. so it can be given
// a ref. Returns class components as is.
const ClassComponent = toClass(FunctionComponent)
```
### ...and more
## API docs
[Read them here](docs/API.md)
## Flow support
[Read the docs](docs/flow.md)
## Translation
[Traditional Chinese](https://github.com/neighborhood999/recompose)
## Why
Forget ES6 classes vs. `createClass()`.
An idiomatic React application consists mostly of function components.
```js
const Greeting = props =>
<p>
Hello, {props.name}!
</p>
```
Function components have several key advantages:
- They help prevent abuse of the `setState()` API, favoring props instead.
- They encourage the ["smart" vs. "dumb" component pattern](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0).
- They encourage code that is more reusable and modular.
- They discourage giant, complicated components that do too many things.
- They allow React to make performance optimizations by avoiding unnecessary checks and memory allocations.
(Note that although Recompose encourages the use of function components whenever possible, it works with normal React components as well.)
### Higher-order components made easy
Most of the time when we talk about composition in React, we're talking about composition of components. For example, a `<Blog>` component may be composed of many `<Post>` components, which are composed of many `<Comment>` components.
Recompose focuses on another unit of composition: **higher-order components** (HoCs). HoCs are functions that accept a base component and return a new component with additional functionality. They can be used to abstract common tasks into reusable pieces.
Recompose provides a toolkit of helper functions for creating higher-order components.
## [Should I use this? Performance and other concerns](docs/performance.md)
## Usage
All functions are available on the top-level export.
```js
import { compose, mapProps, withState /* ... */ } from 'recompose'
```
**Note:** `react` is a _peer dependency_ of Recompose. If you're using `preact`, add this to your `webpack.config.js`:
```js
resolve: {
alias: {
react: "preact"
}
}
```
### Composition
Recompose helpers are designed to be composable:
```js
const BaseComponent = props => {...}
// This will work, but it's tedious
let EnhancedComponent = pure(BaseComponent)
EnhancedComponent = mapProps(/*...args*/)(EnhancedComponent)
EnhancedComponent = withState(/*...args*/)(EnhancedComponent)
// Do this instead
// Note that the order has reversed — props flow from top to bottom
const enhance = compose(
withState(/*...args*/),
mapProps(/*...args*/),
pure
)
const EnhancedComponent = enhance(BaseComponent)
```
Technically, this also means you can use them as decorators (if that's your thing):
```js
@withState(/*...args*/)
@mapProps(/*...args*/)
@pure
class Component extends React.Component {...}
```
### Optimizing bundle size
Since `0.23.1` version recompose got support of ES2015 modules.
To reduce size all you need is to use bundler with tree shaking support
like [webpack 2](https://github.com/webpack/webpack) or [Rollup](https://github.com/rollup/rollup).
#### Using babel-plugin-lodash
[babel-plugin-lodash](https://github.com/lodash/babel-plugin-lodash) is not only limited to [lodash](https://github.com/lodash/lodash). It can be used with `recompose` as well.
This can be done by updating `lodash` config in `.babelrc`.
```diff
{
- "plugins": ["lodash"]
+ "plugins": [
+ ["lodash", { "id": ["lodash", "recompose"] }]
+ ]
}
```
After that, you can do imports like below without actually including the entire library content.
```js
import { compose, mapProps, withState } from 'recompose'
```
### Debugging
It might be hard to trace how does `props` change between HOCs. A useful tip is you can create a debug HOC to print out the props it gets without modifying the base component. For example:
make
```js
const debug = withProps(console.log)
```
then use it between HOCs
```js
const enhance = compose(
withState(/*...args*/),
debug, // print out the props here
mapProps(/*...args*/),
pure
)
```
## Who uses Recompose
If your company or project uses Recompose, feel free to add it to [the official list of users](https://github.com/acdlite/recompose/wiki/Sites-Using-Recompose) by [editing](https://github.com/acdlite/recompose/wiki/Sites-Using-Recompose/_edit) the wiki page.
## Recipes for Inspiration
We have a community-driven Recipes page. It's a place to share and see recompose patterns for inspiration. Please add to it! [Recipes](https://github.com/acdlite/recompose/wiki/Recipes).
## Feedback wanted
Project is still in the early stages. Please file an issue or submit a PR if you have suggestions! Or ping me (Andrew Clark) on [Twitter](https://twitter.com/acdlite).
## Getting Help
**For support or usage questions like “how do I do X with Recompose” and “my code doesn't work”, please search and ask on [StackOverflow with a Recompose tag](http://stackoverflow.com/questions/tagged/recompose?sort=votes&pageSize=50) first.**
We ask you to do this because StackOverflow has a much better job at keeping popular questions visible. Unfortunately good answers get lost and outdated on GitHub.
Some questions take a long time to get an answer. **If your question gets closed or you don't get a reply on StackOverflow for longer than a few days,** we encourage you to post an issue linking to your question. We will close your issue but this will give people watching the repo an opportunity to see your question and reply to it on StackOverflow if they know the answer.
Please be considerate when doing this as this is not the primary purpose of the issue tracker.
### Help Us Help You
On both websites, it is a good idea to structure your code and question in a way that is easy to read to entice people to answer it. For example, we encourage you to use syntax highlighting, indentation, and split text in paragraphs.
Please keep in mind that people spend their free time trying to help you. You can make it easier for them if you provide versions of the relevant libraries and a runnable small project reproducing your issue. You can put your code on [JSBin](http://jsbin.com) or, for bigger projects, on GitHub. Make sure all the necessary dependencies are declared in `package.json` so anyone can run `npm install && npm start` and reproduce your issue.
================================================
FILE: babel.config.js
================================================
module.exports = {
plugins: [['@babel/proposal-class-properties', { loose: true }]],
presets: [['@babel/env', { loose: true }], '@babel/react'],
}
if (process.env.NODE_ENV === 'cjs') {
module.exports.plugins.push('@babel/transform-runtime')
}
================================================
FILE: docs/API.md
================================================
# API
In these API docs, a **higher-order component** (HOC) refers to a function that accepts a single React component and returns a new React component.
```js
const EnhancedComponent = hoc(BaseComponent)
```
This form makes HOCs (sometimes called **enhancers**) composable:
```js
const composedHoc = compose(hoc1, hoc2, hoc3)
// Same as
const composedHoc = BaseComponent => hoc1(hoc2(hoc3(BaseComponent)))
```
Most Recompose helpers are **functions that return higher-order components**:
```js
const hoc = mapProps(ownerProps => childProps)
const EnhancedComponent = hoc(BaseComponent)
// Same as
const EnhancedComponent = mapProps(ownerProps => childProps)(BaseComponent)
```
Some, like `pure`, are higher-order components themselves:
```js
const PureComponent = pure(BaseComponent)
```
## TOC
* [Higher-order components](#higher-order-components)
+ [`mapProps()`](#mapprops)
+ [`withProps()`](#withprops)
+ [`withPropsOnChange()`](#withpropsonchange)
+ [`withHandlers()`](#withhandlers)
+ [`defaultProps()`](#defaultprops)
+ [`renameProp()`](#renameprop)
+ [`renameProps()`](#renameprops)
+ [`flattenProp()`](#flattenprop)
+ [`withState()`](#withstate)
+ [`withStateHandlers()`](#withstatehandlers)
+ [`withReducer()`](#withreducer)
+ [`branch()`](#branch)
+ [`renderComponent()`](#rendercomponent)
+ [`renderNothing()`](#rendernothing)
+ [`shouldUpdate()`](#shouldupdate)
+ [`pure()`](#pure)
+ [`onlyUpdateForKeys()`](#onlyupdateforkeys)
+ [`onlyUpdateForPropTypes()`](#onlyupdateforproptypes)
+ [`withContext()`](#withcontext)
+ [`getContext()`](#getcontext)
+ [`lifecycle()`](#lifecycle)
+ [`toClass()`](#toclass)
+ [`toRenderProps()`](#torenderprops)
+ [`fromRenderProps()`](#fromrenderprops)
* [Static property helpers](#static-property-helpers)
+ [`setStatic()`](#setstatic)
+ [`setPropTypes()`](#setproptypes)
+ [`setDisplayName()`](#setdisplayname)
* [Utilities](#utilities)
+ [`compose()`](#compose)
+ [`getDisplayName()`](#getdisplayname)
+ [`wrapDisplayName()`](#wrapdisplayname)
+ [`shallowEqual()`](#shallowequal)
+ [`isClassComponent()`](#isclasscomponent)
+ [`createSink()`](#createsink)
+ [`componentFromProp()`](#componentfromprop)
+ [`nest()`](#nest)
+ [`hoistStatics()`](#hoiststatics)
* [Observable utilities](#observable-utilities)
+ [`componentFromStream()`](#componentfromstream)
+ [`componentFromStreamWithConfig()`](#componentfromstreamwithconfig)
+ [`mapPropsStream()`](#mappropsstream)
+ [`mapPropsStreamWithConfig()`](#mappropsstreamwithconfig)
+ [`createEventHandler()`](#createeventhandler)
+ [`createEventHandlerWithConfig()`](#createeventhandlerwithconfig)
+ [`setObservableConfig()`](#setobservableconfig)
## Higher-order components
### `mapProps()`
```js
mapProps(
propsMapper: (ownerProps: Object) => Object,
): HigherOrderComponent
```
Accepts a function that maps owner props to a new collection of props that are passed to the base component.
`mapProps()` pairs well with functional utility libraries like [lodash/fp](https://github.com/lodash/lodash/tree/npm/fp). For example, Recompose does not come with a `omitProps()` function, but you can easily build one using lodash-fp's `omit()`:
```js
const omitProps = keys => mapProps(props => omit(keys, props))
// Because of currying in lodash-fp, this is the same as
const omitProps = compose(mapProps, omit)
```
### `withProps()`
```js
withProps(
createProps: (ownerProps: Object) => Object | Object
): HigherOrderComponent
```
Like `mapProps()`, except the newly created props are merged with the owner props.
Instead of a function, you can also pass a props object directly. In this form, it is similar to `defaultProps()`, except the provided props take precedence over props from the owner.
### `withPropsOnChange()`
```js
withPropsOnChange(
shouldMapOrKeys: Array<string> | (props: Object, nextProps: Object) => boolean,
createProps: (ownerProps: Object) => Object
): HigherOrderComponent
```
Like `withProps()`, except the new props are only created when one of the owner props specified by `shouldMapOrKeys` changes. This helps ensure that expensive computations inside `createProps()` are only executed when necessary.
Instead of an array of prop keys, the first parameter can also be a function that returns a boolean, given the current props and the next props. This allows you to customize when `createProps()` should be called.
### `withHandlers()`
```js
withHandlers(
handlerCreators: {
[handlerName: string]: (props: Object) => Function
} |
handlerCreatorsFactory: (initialProps) => {
[handlerName: string]: (props: Object) => Function
}
): HigherOrderComponent
```
Takes an object map of handler creators or a factory function. These are higher-order functions that accept a set of props and return a function handler:
This allows the handler to access the current props via closure, without needing to change its signature.
Handlers are passed to the base component as immutable props, whose identities are preserved across renders. This avoids a common pitfall where functional components create handlers inside the body of the function, which results in a new handler on every render and breaks downstream `shouldComponentUpdate()` optimizations that rely on prop equality. This is the main reason to use `withHandlers` to create handlers instead of using `mapProps` or `withProps`, which will create new handlers every time when it get updated.
Usage example:
```js
const enhance = compose(
withState('value', 'updateValue', ''),
withHandlers({
onChange: props => event => {
props.updateValue(event.target.value)
},
onSubmit: props => event => {
event.preventDefault()
submitForm(props.value)
}
})
)
const Form = enhance(({ value, onChange, onSubmit }) =>
<form onSubmit={onSubmit}>
<label>Value
<input type="text" value={value} onChange={onChange} />
</label>
</form>
)
```
### `defaultProps()`
```js
defaultProps(
props: Object
): HigherOrderComponent
```
Specifies props to be passed by default to the base component. Similar to `withProps()`, except the props from the owner take precedence over props provided to the HoC.
Although it has a similar effect, using the `defaultProps()` HoC is *not* the same as setting the static `defaultProps` property directly on the component.
### `renameProp()`
```js
renameProp(
oldName: string,
newName: string
): HigherOrderComponent
```
Renames a single prop.
Example:
```js
const enhance = compose(
withProps({ 'loadingDataFromApi': true, 'posts': [] }),
renameProp('loadingDataFromApi', 'isLoading'),
renameProp('posts', 'items'),
);
const Posts = enhance(({ isLoading, items }) => (
<div>
<div>Loading: { isLoading ? 'yes' : 'no'}</div>
<ul>
{isLoading && items.map(({ id, title }) => (<li key={id}>{title}</li>))}
</ul>
</div>
));
```
### `renameProps()`
```js
renameProps(
nameMap: { [key: string]: string }
): HigherOrderComponent
```
Renames multiple props, using a map of old prop names to new prop names.
Example:
```js
const enhance = compose(
withProps({ 'loadingDataFromApi': true, 'posts': [] }),
renameProps({ 'loadingDataFromApi': 'isLoading', 'posts': 'items' }),
);
const Posts = enhance(({ isLoading, items }) => (
<div>
<div>Loading: { isLoading ? 'yes' : 'no'}</div>
<ul>
{isLoading && items.map(({ id, title }) => (<li key={id}>{title}</li>))}
</ul>
</div>
));
```
### `flattenProp()`
```js
flattenProp(
propName: string
): HigherOrderComponent
```
Flattens a prop so that its fields are spread out into the props object.
```js
const enhance = compose(
withProps({
object: { a: 'a', b: 'b' },
c: 'c'
}),
flattenProp('object')
)
const Abc = enhance(BaseComponent)
// Base component receives props: { a: 'a', b: 'b', c: 'c', object: { a: 'a', b: 'b' } }
```
An example use case for `flattenProp()` is when receiving fragment data from Relay. Relay fragments are passed as an object of props, which you often want flattened out into its constituent fields:
```js
// The `post` prop is an object with title, author, and content fields
const enhance = flattenProp('post')
const Post = enhance(({ title, content, author }) =>
<article>
<h1>{title}</h1>
<h2>By {author.name}</h2>
<div>{content}</div>
</article>
)
```
### `withState()`
```js
withState(
stateName: string,
stateUpdaterName: string,
initialState: any | (props: Object) => any
): HigherOrderComponent
```
Passes two additional props to the base component: a state value, and a function to update that state value. The state updater has the following signature:
```js
stateUpdater<T>((prevValue: T) => T, ?callback: Function): void
stateUpdater(newValue: any, ?callback: Function): void
```
The first form accepts a function which maps the previous state value to a new state value. You'll likely want to use this state updater along with `withHandlers()` to create specific updater functions. For example, to create a HoC that adds basic counting functionality to a component:
```js
const addCounting = compose(
withState('counter', 'setCounter', 0),
withHandlers({
increment: ({ setCounter }) => () => setCounter(n => n + 1),
decrement: ({ setCounter }) => () => setCounter(n => n - 1),
reset: ({ setCounter }) => () => setCounter(0)
})
)
```
The second form accepts a single value, which is used as the new state.
Both forms accept an optional second parameter, a callback function that will be executed once `setState()` is completed and the component is re-rendered.
An initial state value is required. It can be either the state value itself, or a function that returns an initial state given the initial props.
### `withStateHandlers()`
```js
withStateHandlers(
initialState: Object | (props: Object) => any,
stateUpdaters: {
[key: string]: (state:Object, props:Object) => (...payload: any[]) => Object
}
)
```
Passes state object properties and immutable updater functions
in a form of `(...payload: any[]) => Object` to the base component.
Every state updater function accepts state, props and payload and must return a new state or undefined. The new state is shallowly merged with the previous state.
Returning undefined does not cause a component rerender.
Example:
```js
const Counter = withStateHandlers(
({ initialCounter = 0 }) => ({
counter: initialCounter,
}),
{
incrementOn: ({ counter }) => (value) => ({
counter: counter + value,
}),
decrementOn: ({ counter }) => (value) => ({
counter: counter - value,
}),
resetCounter: (_, { initialCounter = 0 }) => () => ({
counter: initialCounter,
}),
}
)(
({ counter, incrementOn, decrementOn, resetCounter }) =>
<div>
<Button onClick={() => incrementOn(2)}>Inc</Button>
<Button onClick={() => decrementOn(3)}>Dec</Button>
<Button onClick={resetCounter}>Reset</Button>
</div>
)
```
### `withReducer()`
```js
withReducer<S, A>(
stateName: string,
dispatchName: string,
reducer: (state: S, action: A) => S,
initialState: S | (ownerProps: Object) => S
): HigherOrderComponent
```
Similar to `withState()`, but state updates are applied using a reducer function. A reducer is a function that receives a state and an action, and returns a new state.
Passes two additional props to the base component: a state value, and a dispatch method. The dispatch method has the following signature:
```js
dispatch(action: Object, ?callback: Function): void
```
It sends an action to the reducer, after which the new state is applied. It also accepts an optional second parameter, a callback function with the new state as its only argument.
### `branch()`
```js
branch(
test: (props: Object) => boolean,
left: HigherOrderComponent,
right: ?HigherOrderComponent
): HigherOrderComponent
```
Accepts a test function and two higher-order components. The test function is passed the props from the owner. If it returns true, the `left` higher-order component is applied to `BaseComponent`; otherwise, the `right` higher-order component is applied. If the `right` is not supplied, it will by default render the wrapped component.
### `renderComponent()`
```js
renderComponent(
Component: ReactClass | ReactFunctionalComponent | string
): HigherOrderComponent
```
Takes a component and returns a higher-order component version of that component.
This is useful in combination with another helper that expects a higher-order component, like `branch()`:
```js
// `isLoading()` is a function that returns whether or not the component
// is in a loading state
const spinnerWhileLoading = isLoading =>
branch(
isLoading,
renderComponent(Spinner) // `Spinner` is a React component
)
// Now use the `spinnerWhileLoading()` helper to add a loading spinner to any
// base component
const enhance = spinnerWhileLoading(
props => !(props.title && props.author && props.content)
)
const Post = enhance(({ title, author, content }) =>
<article>
<h1>{title}</h1>
<h2>By {author.name}</h2>
<div>{content}</div>
</article>
)
```
### `renderNothing()`
```js
renderNothing: HigherOrderComponent
```
A higher-order component that always renders `null`.
This is useful in combination with another helper that expects a higher-order component, like `branch()`:
```js
// `hasNoData()` is a function that returns true if the component has
// no data
const hideIfNoData = hasNoData =>
branch(
hasNoData,
renderNothing
)
// Now use the `hideIfNoData()` helper to hide any base component
const enhance = hideIfNoData(
props => !(props.title && props.author && props.content)
)
const Post = enhance(({ title, author, content }) =>
<article>
<h1>{title}</h1>
<h2>By {author.name}</h2>
<div>{content}</div>
</article>
)
```
### `shouldUpdate()`
```js
shouldUpdate(
test: (props: Object, nextProps: Object) => boolean
): HigherOrderComponent
```
Higher-order component version of [`shouldComponentUpdate()`](https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate). The test function accepts both the current props and the next props.
### `pure()`
```js
pure: HigherOrderComponent
```
Prevents the component from updating unless a prop has changed. Uses `shallowEqual()` to test for changes.
### `onlyUpdateForKeys()`
```js
onlyUpdateForKeys(
propKeys: Array<string>
): HigherOrderComponent
```
Prevents the component from updating unless a prop corresponding to one of the given keys has updated. Uses `shallowEqual()` to test for changes.
This is a much better optimization than the popular approach of using PureRenderMixin, `shouldPureComponentUpdate()`, or Recompose's own `pure()` helper, because those tools compare *every* prop, whereas `onlyUpdateForKeys()` only cares about the props that you specify.
Example:
```js
/**
* If the owner passes unnecessary props (say, an array of comments), it will
* not lead to wasted render cycles.
*
* Goes well with destructuring because it's clear which props the component
* actually cares about.
*/
const enhance = onlyUpdateForKeys(['title', 'content', 'author'])
const Post = enhance(({ title, content, author }) =>
<article>
<h1>{title}</h1>
<h2>By {author.name}</h2>
<div>{content}</div>
</article>
)
```
### `onlyUpdateForPropTypes()`
```js
onlyUpdateForPropTypes: HigherOrderComponent
```
Works like `onlyUpdateForKeys()`, but prop keys are inferred from the `propTypes` of the base component. Useful in conjunction with `setPropTypes()`.
If the base component does not have any `propTypes`, the component will never receive any updates. This probably isn't the expected behavior, so a warning is printed to the console.
```js
import PropTypes from 'prop-types'; // You need to import prop-types. See https://facebook.github.io/react/docs/typechecking-with-proptypes.html
const enhance = compose(
onlyUpdateForPropTypes,
setPropTypes({
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
author: PropTypes.object.isRequired
})
)
const Post = enhance(({ title, content, author }) =>
<article>
<h1>{title}</h1>
<h2>By {author.name}</h2>
<div>{content}</div>
</article>
)
```
### `withContext()`
```js
withContext(
childContextTypes: Object,
getChildContext: (props: Object) => Object
): HigherOrderComponent
```
Provides context to the component's children. `childContextTypes` is an object of React prop types. `getChildContext()` is a function that returns the child context. Use along with `getContext()`.
### `getContext()`
```js
getContext(
contextTypes: Object
): HigherOrderComponent
```
Gets values from context and passes them along as props. Use along with `withContext()`.
### `lifecycle()`
```js
lifecycle(
spec: Object,
): HigherOrderComponent
```
A higher-order component version of [`React.Component()`](https://facebook.github.io/react/docs/react-api.html#react.component). It supports the entire `Component` API, except the `render()` method, which is implemented by default (and overridden if specified; an error will be logged to the console). You should use this helper as an escape hatch, in case you need to access component lifecycle methods.
Any state changes made in a lifecycle method, by using `setState`, will be propagated to the wrapped component as props.
Example:
```js
const PostsList = ({ posts }) => (
<ul>{posts.map(p => <li>{p.title}</li>)}</ul>
)
const PostsListWithData = lifecycle({
componentDidMount() {
fetchPosts().then(posts => {
this.setState({ posts });
})
}
})(PostsList);
```
### `toClass()`
```js
toClass: HigherOrderComponent
```
Takes a function component and wraps it in a class. This can be used as a fallback for libraries that need to add a ref to a component, like Relay.
If the base component is already a class, it returns the given component.
### `toRenderProps()`
```js
toRenderProps(
hoc: HigherOrderComponent
): ReactFunctionalComponent
```
Creates a component that accepts a function as a children with the high-order component applied to it.
Example:
```js
const enhance = withProps(({ foo }) => ({ fooPlusOne: foo + 1 }))
const Enhanced = toRenderProps(enhance)
<Enhanced foo={1}>{({ fooPlusOne }) => <h1>{fooPlusOne}</h1>}</Enhanced>
// renders <h1>2</h1>
```
### `fromRenderProps()`
```js
fromRenderProps(
RenderPropsComponent: ReactClass | ReactFunctionalComponent,
propsMapper: (...props: any[]) => Object,
renderPropName?: string
): HigherOrderComponent
```
Takes a **render props** component and a function that maps props to a new collection of props that are passed to the base component.
The default value of third parameter (`renderPropName`) is `children`. You can use any prop (e.g., `render`) for render props component to work.
> Check the official documents [Render Props](https://reactjs.org/docs/render-props.html#using-props-other-than-render) for more details.
```js
import { fromRenderProps } from 'recompose';
const { Consumer: ThemeConsumer } = React.createContext({ theme: 'dark' });
const { Consumer: I18NConsumer } = React.createContext({ i18n: 'en' });
const RenderPropsComponent = ({ render, value }) => render({ value: 1 });
const EnhancedApp = compose(
// Context (Function as Child Components)
fromRenderProps(ThemeConsumer, ({ theme }) => ({ theme })),
fromRenderProps(I18NConsumer, ({ i18n }) => ({ locale: i18n })),
// Render props
fromRenderProps(RenderPropsComponent, ({ value }) => ({ value }), 'render'),
)(App);
// Same as
const EnhancedApp = () => (
<ThemeConsumer>
{({ theme }) => (
<I18NConsumer>
{({ i18n }) => (
<RenderPropsComponent
render={({ value }) => (
<App theme={theme} locale={i18n} value={value} />
)}
/>
)}
</I18NConsumer>
)}
</ThemeConsumer>
)
```
## Static property helpers
These functions look like higher-order component helpers — they are curried and component-last. However, rather than returning a new component, they mutate the base component by setting or overriding a static property.
### `setStatic()`
```js
setStatic(
key: string,
value: any
): HigherOrderComponent
```
Assigns a value to a static property on the base component.
### `setPropTypes()`
```js
setPropTypes(
propTypes: Object
): HigherOrderComponent
```
Assigns to the `propTypes` property on the base component.
### `setDisplayName()`
```js
setDisplayName(
displayName: string
): HigherOrderComponent
```
Assigns to the `displayName` property on the base component.
## Utilities
Recompose also includes some additional helpers that aren't higher-order components, but are still useful.
### `compose()`
```js
compose(...functions: Array<Function>): Function
```
Use to compose multiple higher-order components into a single higher-order component. This works exactly like the function of the same name in Redux, or lodash's `flowRight()`.
### `getDisplayName()`
```js
getDisplayName(
component: ReactClass | ReactFunctionalComponent
): string
```
Returns the display name of a React component. Falls back to `'Component'`.
### `wrapDisplayName()`
```js
wrapDisplayName(
component: ReactClass | ReactFunctionalComponent,
wrapperName: string
): string
```
Returns a wrapped version of a React component's display name. For instance, if the display name of `component` is `'Post'`, and `wrapperName` is `'mapProps'`, the return value is `'mapProps(Post)'`. Most Recompose higher-order components use `wrapDisplayName()`.
### `shallowEqual()`
```js
shallowEqual(a: Object, b: Object): boolean
```
Returns true if objects are shallowly equal.
### `isClassComponent()`
```js
isClassComponent(value: any): boolean
```
Returns true if the given value is a React component class.
### `createSink()`
```js
createSink(callback: (props: Object) => void): ReactClass
```
Creates a component that renders nothing (null) but calls a callback when receiving new props.
### `componentFromProp()`
```js
componentFromProp(propName: string): ReactFunctionalComponent
```
Creates a component that accepts a component as a prop and renders it with the remaining props.
Example:
```js
const enhance = defaultProps({ component: 'button' })
const Button = enhance(componentFromProp('component'))
<Button foo="bar" /> // renders <button foo="bar" />
<Button component="a" foo="bar" /> // renders <a foo="bar" />
<Button component={Link} foo="bar" /> // renders <Link foo="bar" />
```
### `nest()`
```js
nest(
...Components: Array<ReactClass | ReactFunctionalComponent | string>
): ReactClass
```
Composes components by nesting each one inside the previous. For example:
```js
// Given components A, B, and C
const ABC = nest(A, B, C)
<ABC pass="through">Child</ABC>
// Effectively the same as
<A pass="through">
<B pass="through">
<C pass="through">
Child
</C>
</B>
</A>
```
### `hoistStatics()`
```js
hoistStatics(
hoc: HigherOrderComponent,
blacklist: Object
): HigherOrderComponent
```
Augments a higher-order component so that when used, it copies non-react static properties from the base component to the new component. This is helpful when using Recompose with libraries like Relay.
Note that this only hoists _non-react_ statics. The following static properties will not be hoisted: `childContextTypes`, `contextTypes`, `defaultProps`, `displayName`, `getDefaultProps`, `mixins`, `propTypes`, and `type`. The following native static methods will also be ignored: `name`, `length`, `prototype`, `caller`, `arguments`, and `arity`.
You can exclude more static properties by passing them as `blacklist` object:
```js
hoistStatics(EnhancedComponent, { foo: true })(BaseComponent) // Exclude `foo`
```
## Observable utilities
It turns out that much of the React Component API can be expressed in terms of observables:
- Instead of `setState()`, combine multiple streams together.
- Instead of `getInitialState()`, use `startWith()` or `concat()`.
- Instead of `shouldComponentUpdate()`, use `distinctUntilChanged()`, `debounce()`, etc.
Other benefits include:
- No distinction between state and props – everything is a stream.
- No need to worry about unsubscribing from event listeners. Subscriptions are handled for you.
- Sideways data loading is trivial – just combine the props stream with an external stream.
- Access to an ecosystem of observable libraries, such as RxJS.
**Recompose's observable utilities can be configured to work with any observable or stream-like library. See [`setObservableConfig()`](#setobservableconfig) below for details.**
### `componentFromStream()`
```js
componentFromStream(
propsToReactNode: (props$: Observable<object>) => Observable<ReactNode>
): ReactComponent
```
Creates a React component by mapping an observable stream of props to a stream of React nodes (vdom).
You can think of `propsToReactNode` as a function `f` such that
```js
const vdom$ = f(props$)
```
where `props$` is a stream of props and `vdom$` is a stream of React nodes. This formulation similar to the popular notion of React views as a function, often communicated as
```
v = f(d)
```
Example:
```js
const Counter = componentFromStream(props$ => {
const { handler: increment, stream: increment$ } = createEventHandler()
const { handler: decrement, stream: decrement$ } = createEventHandler()
const count$ = Observable.merge(
increment$.mapTo(1),
decrement$.mapTo(-1)
)
.startWith(0)
.scan((count, n) => count + n, 0)
return props$.combineLatest(
count$,
(props, count) =>
<div {...props}>
Count: {count}
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
)
})
```
### `componentFromStreamWithConfig()`
```js
componentFromStreamWithConfig<Stream>(
config: {
fromESObservable<T>: ?(observable: Observable<T>) => Stream<T>,
toESObservable<T>: ?(stream: Stream<T>) => Observable<T>,
}
) => (
propsToReactNode: (props$: Stream<object>) => Stream<ReactNode>
): ReactComponent
```
Alternative to `componentFromStream()` that accepts an observable config and returns a customized `componentFromStream()` which uses the specified observable library. This option is recommended if you want to avoid global state with `setObservableConfig()`.
**Note: The following configuration modules are not included in the main export. You must import them individually, as shown in the examples.**
#### RxJS
```js
import rxjsConfig from 'recompose/rxjsObservableConfig'
const componentFromStream = componentFromStreamWithConfig(rxjsConfig)
```
#### RxJS 4 (legacy)
```js
import rxjs4Config from 'recompose/rxjs4ObservableConfig'
const componentFromStream = componentFromStreamWithConfig(rxjs4Config)
```
#### most
```js
import mostConfig from 'recompose/mostObservableConfig'
const componentFromStream = componentFromStreamWithConfig(mostConfig)
```
#### xstream
```js
import xstreamConfig from 'recompose/xstreamObservableConfig'
const componentFromStream = componentFromStreamWithConfig(xstreamConfig)
```
#### Bacon
```js
import baconConfig from 'recompose/baconObservableConfig'
const componentFromStream = componentFromStreamWithConfig(baconConfig)
```
#### Kefir
```js
import kefirConfig from 'recompose/kefirObservableConfig'
const componentFromStream = componentFromStreamWithConfig(kefirConfig)
```
#### Flyd
```js
import flydConfig from 'recompose/flydObservableConfig'
const componentFromStream = componentFromStreamWithConfig(flydConfig)
```
### `mapPropsStream()`
```js
mapPropsStream(
ownerPropsToChildProps: (props$: Observable<object>) => Observable<object>,
BaseComponent: ReactElementType
): ReactComponent
```
A higher-order component version of `componentFromStream()` — accepts a function that maps an observable stream of owner props to a stream of child props, rather than directly to a stream of React nodes. The child props are then passed to a base component.
You may want to use this version to interoperate with other Recompose higher-order component helpers.
### `mapPropsStreamWithConfig()`
```js
mapPropsStreamWithConfig<Stream>(
config: {
fromESObservable<T>: ?(observable: Observable<T>) => Stream<T>,
toESObservable<T>: ?(stream: Stream<T>) => Observable<T>,
},
) => (
ownerPropsToChildProps: (props$: Stream<object>) => Stream<object>,
BaseComponent: ReactElementType
): ReactComponent
```
Alternative to `mapPropsStream()` that accepts a observable config and returns a customized `mapPropsStream()` that uses the specified observable library. See `componentFromStreamWithConfig()` above.
```js
const enhance = mapPropsStream(props$ => {
const timeElapsed$ = Observable.interval(1000)
return props$.combineLatest(timeElapsed$, (props, timeElapsed) => ({
...props,
timeElapsed
}))
})
const Timer = enhance(({ timeElapsed }) =>
<div>Time elapsed: {timeElapsed}</div>
)
```
### `createEventHandler()`
```js
createEventHandler<T>(): {
handler: (value: T) => void,
stream: Observable<T>
}
```
Returns an object with properties `handler` and `stream`. `stream` is an observable sequence, and `handler` is a function that pushes new values onto the sequence. Useful for creating event handlers like `onClick`.
### `createEventHandlerWithConfig()`
```js
createEventHandlerWithConfig<T>(
config: {
fromESObservable<T>: ?(observable: Observable<T>) => Stream<T>,
toESObservable<T>: ?(stream: Stream<T>) => Observable<T>,
}
) => (): {
handler: (value: T) => void,
stream: Observable<T>
}
```
Alternative to `createEventHandler()` that accepts an observable config and returns a customized `createEventHandler()` that uses the specified observable library. See `componentFromStreamWithConfig()` above.
### `setObservableConfig()`
```js
setObservableConfig<Stream>({
fromESObservable<T>: ?(observable: Observable<T>) => Stream<T>,
toESObservable<T>: ?(stream: Stream<T>) => Observable<T>
})
```
**Note: `setObservableConfig()` uses global state, and could break apps if used inside a package intended to be shared. See `componentFromStreamWithConfig()` and `mapPropsStreamWithConfig()` as alternatives for package authors.**
Observables in Recompose are plain objects that conform to the [ES Observable proposal](https://github.com/zenparsing/es-observable). Usually, you'll want to use them alongside an observable library like RxJS so that you have access to its suite of operators. By default, this requires you to convert the observables provided by Recompose before applying any transforms:
```js
mapPropsStream(props$ => {
const rxjsProps$ = Rx.Observable.from(props$)
// ...now you can use map, filter, scan, etc.
return transformedProps$
})
```
This quickly becomes tedious. Rather than performing this transform for each stream individually, `setObservableConfig()` sets a global observable transform that is applied automatically.
```js
import Rx from 'rxjs'
import { setObservableConfig } from 'recompose'
setObservableConfig({
// Converts a plain ES observable to an RxJS 5 observable
fromESObservable: Rx.Observable.from
})
```
In addition to `fromESObservable`, the config object also accepts `toESObservable`, which converts a stream back into an ES observable. Because RxJS 5 observables already conform to the ES observable spec, `toESObservable` is not necessary in the above example. However, it is required for libraries like RxJS 4 or xstream, whose streams do not conform to the ES observable spec.
Fortunately, you likely don't need to worry about how to configure Recompose for your favorite stream library, because Recompose provides drop-in configuration for you.
**Note: The following configuration modules are not included in the main export. You must import them individually, as shown in the examples.**
#### RxJS
```js
import rxjsconfig from 'recompose/rxjsObservableConfig'
setObservableConfig(rxjsconfig)
```
#### RxJS 4 (legacy)
```js
import rxjs4config from 'recompose/rxjs4ObservableConfig'
setObservableConfig(rxjs4config)
```
#### most
```js
import mostConfig from 'recompose/mostObservableConfig'
setObservableConfig(mostConfig)
```
#### xstream
```js
import xstreamConfig from 'recompose/xstreamObservableConfig'
setObservableConfig(xstreamConfig)
```
#### Bacon
```js
import baconConfig from 'recompose/baconObservableConfig'
setObservableConfig(baconConfig)
```
#### Kefir
```js
import kefirConfig from 'recompose/kefirObservableConfig'
setObservableConfig(kefirConfig)
```
#### Flyd
```js
import flydConfig from 'recompose/flydObservableConfig'
setObservableConfig(flydConfig)
```
================================================
FILE: docs/flow.md
================================================
# 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.

## 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.
================================================
FILE: docs/performance.md
================================================
# Should I use this? Performance and other concerns
If function composition doesn't scare you, then yes, I think so. I believe using higher-order component helpers leads to smaller, more focused components, and provides a better programming model than using classes for operations—like `mapProps()` or `shouldUpdate()`—that aren't inherently class-y.
That being said, any abstraction over an existing API is going to come with trade-offs. There is a performance overhead when introducing a new component to the tree. I suspect this cost is negligible compared to the gains achieved by blocking subtrees from re-rendering using `shouldComponentUpdate()`—which Recompose makes easy with its `shouldUpdate()` and `onlyUpdateForKeys()` helpers. In the future, I'll work on some benchmarks so we know what we're dealing with.
However, many of Recompose's higher-order component helpers are implemented using stateless function components rather than class components. Eventually, React will include optimizations for stateless components.
================================================
FILE: package.json
================================================
{
"name": "recompose-build",
"private": true,
"author": "Andrew Clark <acdlite@me.com>",
"repository": {
"type": "git",
"url": "git://github.com/acdlite/recompose.git"
},
"license": "MIT",
"scripts": {
"lint": "eslint scripts src",
"build:recompose": "cross-env PACKAGE_NAME=recompose rollup --config scripts/rollup.config.js",
"test": "jest && flow check && cross-env SNAPSHOT=match npm run build:recompose",
"test:watch": "cross-env BABEL_ENV=cjs jest --watch",
"release": "node scripts/release.js",
"postinstall": "node scripts/installNestedPackageDeps.js",
"format": "prettier --semi false --trailing-comma es5 --single-quote --write 'scripts/*.js' 'src/packages/*/*.js' 'src/packages/*/!(node_modules)/**/*.js'",
"precommit": "lint-staged",
"prepush": "yarn test"
},
"jest": {
"testMatch": [
"<rootDir>/src/**/__tests__/**/*-test.js"
],
"coverageReporters": [
"text-summary",
"lcov"
],
"setupTestFrameworkScriptFile": "<rootDir>/scripts/jest.setup.js"
},
"lint-staged": {
"*.js": [
"prettier --semi false --trailing-comma es5 --single-quote --write",
"eslint --fix",
"git add"
]
},
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.0.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^9.0.0",
"babel-jest": "^22.4.3",
"baconjs": "^0.7.84",
"chalk": "^1.1.1",
"change-case": "^2.3.1",
"codecov": "^1.0.1",
"create-react-class": "^15.5.0",
"cross-env": "^4.0.0",
"enzyme": "^3.3.0",
"eslint": "^5.3.0",
"eslint-config-airbnb": "^17.0.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-prettier": "^2.0.1",
"eslint-plugin-react": "^7.10.0",
"flow-bin": "^0.72.0",
"flyd": "^0.2.4",
"husky": "^0.13.3",
"jest": "^22.4.3",
"kefir": "^3.2.3",
"lint-staged": "^3.4.0",
"most": "^1.0.2",
"prettier": "^1.2.2",
"prop-types": "^15.6.1",
"react": "^16.3.1",
"react-dom": "^16.3.1",
"readline-sync": "^1.2.21",
"rollup": "^0.65.0",
"rollup-plugin-babel": "^4.0.1",
"rollup-plugin-commonjs": "^9.1.6",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-size-snapshot": "^0.6.1",
"rollup-plugin-uglify": "^4.0.0",
"rx": "^4.1.0",
"rxjs": "^5.0.0",
"shelljs": "^0.6.0",
"sinon": "^1.17.1",
"webpack": "^2.4.1",
"xstream": "^5.0.5"
},
"devEngines": {
"node": "5.x",
"npm": "3.x"
},
"dependencies": {
"enzyme-adapter-react-16": "^1.1.1"
}
}
================================================
FILE: scripts/getPackageNames.js
================================================
const fs = require('fs')
const path = require('path')
exports.PACKAGES_SRC_DIR = './src/packages'
exports.PACKAGES_OUT_DIR = './lib/packages'
let names
exports.getPackageNames = () => {
if (!names) {
names = fs.readdirSync(exports.PACKAGES_SRC_DIR).filter(file => {
try {
const packageJsonPath = path.resolve(
exports.PACKAGES_SRC_DIR,
file,
'package.json'
)
return fs.statSync(packageJsonPath).isFile()
} catch (error) {
return false
}
})
}
return names
}
================================================
FILE: scripts/installNestedPackageDeps.js
================================================
const path = require('path')
const { exec } = require('shelljs')
const { getPackageNames, PACKAGES_SRC_DIR } = require('./getPackageNames.js')
const packageNames = getPackageNames()
packageNames.forEach(packageName => {
const sourceDir = path.resolve(PACKAGES_SRC_DIR, packageName)
exec(`cd ${sourceDir} && yarn`, { async: true })
})
================================================
FILE: scripts/jest.setup.js
================================================
/* eslint-disable */
jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000
import Enzyme from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
Enzyme.configure({ adapter: new Adapter() })
================================================
FILE: scripts/release.js
================================================
/* eslint global-require: 0 */
/* eslint-disable import/no-dynamic-require, no-console */
const fs = require('fs')
const path = require('path')
const { exec, exit, rm, cp, test } = require('shelljs')
const chalk = require('chalk')
const { flowRight: compose } = require('lodash')
const readline = require('readline-sync')
const semver = require('semver')
const { pascalCase } = require('change-case')
const BIN = './node_modules/.bin'
const {
PACKAGES_SRC_DIR,
PACKAGES_OUT_DIR,
getPackageNames,
} = require('./getPackageNames')
const BASE_PACKAGE_LOC = '../src/basePackage.json'
const consoleLog = console.log.bind(console)
const log = compose(consoleLog, chalk.bold)
const logSuccess = compose(consoleLog, chalk.green.bold)
const logError = compose(consoleLog, chalk.red.bold)
const writeFile = (filepath, string) =>
fs.writeFileSync(filepath, string, 'utf8')
try {
if (exec('git diff-files --quiet').code !== 0) {
logError(
'You have unsaved changes in the working tree. ' +
'Commit or stash changes before releasing.'
)
exit(1)
}
const packageNames = getPackageNames()
let packageName = readline.question('Name of package to release: ')
while (!packageNames.includes(packageName)) {
packageName = readline.question(
`The package "${packageName}" does not exist in this project. ` +
'Choose again: '
)
}
const libraryName = pascalCase(packageName)
const versionLoc = path.resolve(PACKAGES_SRC_DIR, packageName, 'VERSION')
const version = fs.readFileSync(versionLoc, 'utf8').trim()
let nextVersion = readline.question(
`Next version of ${packageName} (current version is ${version}): `
)
while (
!(
!nextVersion ||
(semver.valid(nextVersion) && semver.gt(nextVersion, version))
)
) {
nextVersion = readline.question(
`Must provide a valid version that is greater than ${version}, ` +
'or leave blank to skip: '
)
}
log('Running tests...')
if (exec('yarn run lint && yarn test').code !== 0) {
logError('The test command did not exit cleanly. Aborting release.')
exit(1)
}
logSuccess('Tests were successful.')
const sourceDir = path.resolve(PACKAGES_SRC_DIR, packageName)
const outDir = path.resolve(PACKAGES_OUT_DIR, packageName)
log('Cleaning destination directory...')
rm('-rf', outDir)
log('Compiling source files...')
exec(
'cross-env NODE_ENV=cjs ' +
`${path.resolve(BIN)}/babel ${sourceDir} ` +
`--out-dir ${path.resolve(
outDir
)} --ignore="**/__tests__/**,**/node_modules/**"`
)
log('Copying additional project files...')
const additionalProjectFiles = ['README.md', '.npmignore']
additionalProjectFiles.forEach(filename => {
const src = path.resolve(sourceDir, filename)
if (!test('-e', src)) return
cp('-Rf', src, outDir)
})
log('Generating package.json...')
const packageConfig = Object.assign(
{ name: packageName, version: nextVersion },
require(BASE_PACKAGE_LOC),
require(path.resolve(sourceDir, 'package.json'))
)
writeFile(
path.resolve(outDir, 'package.json'),
JSON.stringify(packageConfig, null, 2)
)
log('Copying license...')
cp('-f', 'LICENSE.md', outDir)
log(`Building ${packageName}...`)
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.')
exit(0)
}
log('Publishing...')
if (exec(`cd ${outDir} && npm publish`).code !== 0) {
logError('Publish failed. Aborting release.')
exit(1)
}
logSuccess(`${packageName}@${nextVersion} was successfully published.`)
log('Updating VERSION file...')
writeFile(versionLoc, `${nextVersion}\n`)
log('Committing changes...')
const newTagName = `v${nextVersion}`
exec(`git add ${versionLoc}`)
exec(`git commit -m "${packageName} ${newTagName}"`)
if (packageName === 'recompose') {
log(`Tagging release... (${newTagName})`)
exec(`git tag ${newTagName}`)
}
log('Pushing to GitHub...')
exec('git push')
exec('git push --tags')
logSuccess('Done.')
} catch (error) {
logError('Release failed due to an error', error)
}
================================================
FILE: scripts/rollup.config.js
================================================
import path from 'path'
import nodeResolve from 'rollup-plugin-node-resolve'
import babel from 'rollup-plugin-babel'
import replace from 'rollup-plugin-replace'
import commonjs from 'rollup-plugin-commonjs'
import { uglify } from 'rollup-plugin-uglify'
import { sizeSnapshot } from 'rollup-plugin-size-snapshot'
import { pascalCase } from 'change-case'
const { PACKAGES_SRC_DIR, PACKAGES_OUT_DIR } = require('./getPackageNames')
const packageName = process.env.PACKAGE_NAME
const libraryName = pascalCase(packageName)
const input = `./${path.join(PACKAGES_SRC_DIR, packageName, 'index.js')}`
const outDir = path.join(PACKAGES_OUT_DIR, packageName, 'dist')
const isExternal = id => !id.startsWith('.') && !id.startsWith('/')
const getBabelOptions = ({ useESModules }) => ({
exclude: '**/node_modules/**',
runtimeHelpers: true,
plugins: [['@babel/transform-runtime', { useESModules }]],
})
const matchSnapshot = process.env.SNAPSHOT === 'match'
export default [
{
input,
output: {
file: `${outDir}/${libraryName}.umd.js`,
format: 'umd',
name: libraryName,
globals: {
react: 'React',
},
},
external: ['react'],
plugins: [
nodeResolve(),
babel(getBabelOptions({ useESModules: true })),
commonjs(),
replace({ 'process.env.NODE_ENV': JSON.stringify('development') }),
sizeSnapshot({ matchSnapshot }),
],
},
{
input,
output: {
file: `${outDir}/${libraryName}.min.js`,
format: 'umd',
name: libraryName,
globals: {
react: 'React',
},
},
external: ['react'],
plugins: [
nodeResolve(),
babel(getBabelOptions({ useESModules: true })),
commonjs(),
replace({ 'process.env.NODE_ENV': JSON.stringify('production') }),
sizeSnapshot({ matchSnapshot }),
uglify(),
],
},
{
input,
output: {
file: `${outDir}/${libraryName}.cjs.js`,
format: 'cjs',
},
external: isExternal,
plugins: [babel(getBabelOptions({ useESModules: false }))],
},
{
input,
output: {
file: `${outDir}/${libraryName}.esm.js`,
format: 'es',
},
external: isExternal,
plugins: [
babel(getBabelOptions({ useESModules: true })),
sizeSnapshot({ matchSnapshot }),
],
},
]
================================================
FILE: src/basePackage.json
================================================
{
"author": "Andrew Clark <acdlite@me.com>",
"repository": {
"type": "git",
"url": "https://github.com/acdlite/recompose.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/acdlite/recompose/issues"
},
"homepage": "https://github.com/acdlite/recompose"
}
================================================
FILE: src/packages/recompose/.npmignore
================================================
/**/__tests__
================================================
FILE: src/packages/recompose/README.md
================================================
recompose
=========
[](https://www.npmjs.com/package/recompose)
Recompose is a React utility belt for function components and higher-order components. See the [GitHub project page](https://github.com/acdlite/recompose) for more information.
================================================
FILE: src/packages/recompose/VERSION
================================================
0.30.0
================================================
FILE: src/packages/recompose/__tests__/branch-test.js
================================================
import sinon from 'sinon'
import React from 'react'
import { mount } from 'enzyme'
import { branch, compose, withState, withProps } from '../'
test('branch tests props and applies one of two HoCs, for true and false', () => {
const SayMyName = compose(
withState('isBad', 'updateIsBad', false),
branch(
props => props.isBad,
withProps({ name: 'Heisenberg' }),
withProps({ name: 'Walter' })
)
)(({ isBad, name, updateIsBad }) =>
<div>
<div className="isBad">
{isBad ? 'true' : 'false'}
</div>
<div className="name">
{name}
</div>
<button onClick={() => updateIsBad(b => !b)}>Toggle</button>
</div>
)
expect(SayMyName.displayName).toBe('withState(branch(Component))')
const wrapper = mount(<SayMyName />)
const getIsBad = () => wrapper.find('.isBad').text()
const getName = () => wrapper.find('.name').text()
const toggle = wrapper.find('button')
expect(getIsBad()).toBe('false')
expect(getName()).toBe('Walter')
toggle.simulate('click')
expect(getIsBad()).toBe('true')
expect(getName()).toBe('Heisenberg')
})
test('branch defaults third argument to identity function', () => {
const Left = () => <div className="left">Left</div>
const Right = () => <div className="right">Right</div>
const BranchedComponent = branch(
() => false,
() => props => <Left {...props} />
)(Right)
const wrapper = mount(<BranchedComponent />)
const right = wrapper.find('.right').text()
expect(right).toBe('Right')
})
test('branch third argument should not cause console error', () => {
const error = sinon.stub(console, 'error')
const Component = () => <div className="right">Component</div>
const BranchedComponent = branch(() => false, v => v, v => v)(Component)
mount(<BranchedComponent />)
expect(error.called).toBe(false)
/* eslint-disable */
error.restore()
/* eslint-enable */
})
================================================
FILE: src/packages/recompose/__tests__/componentFromProp-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import { componentFromProp } from '../'
test('componentFromProp creates a component that takes a component as a prop and renders it with the rest of the props', () => {
const Container = componentFromProp('component')
expect(Container.displayName).toBe('componentFromProp(component)')
const Component = ({ pass }) =>
<div>
Pass: {pass}
</div>
const wrapper = mount(<Container component={Component} pass="through" />)
const div = wrapper.find('div')
expect(div.text()).toBe('Pass: through')
})
================================================
FILE: src/packages/recompose/__tests__/componentFromStream-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import { Observable, Subject } from 'rxjs'
import sinon from 'sinon'
import rxjsConfig from '../rxjsObservableConfig'
import { componentFromStreamWithConfig } from '../componentFromStream'
const componentFromStream = componentFromStreamWithConfig(rxjsConfig)
test('componentFromStream creates a component from a prop stream transformation', () => {
const Double = componentFromStream(props$ =>
props$.map(({ n }) =>
<div>
{n * 2}
</div>
)
)
const wrapper = mount(<Double n={112} />)
const div = wrapper.find('div')
expect(div.text()).toBe('224')
wrapper.setProps({ n: 358 })
expect(div.text()).toBe('716')
})
test('componentFromStream unsubscribes from stream before unmounting', () => {
let subscriptions = 0
const vdom$ = new Observable(observer => {
subscriptions += 1
observer.next(<div />)
return {
unsubscribe() {
subscriptions -= 1
},
}
})
const Div = componentFromStream(() => vdom$)
const wrapper = mount(<Div />)
expect(subscriptions).toBe(1)
wrapper.unmount()
expect(subscriptions).toBe(0)
})
test('componentFromStream renders nothing until the stream emits a value', () => {
const vdom$ = new Subject()
const Div = componentFromStream(() => vdom$.mapTo(<div />))
const wrapper = mount(<Div />)
expect(wrapper.find('div').length).toBe(0)
vdom$.next()
wrapper.update()
expect(wrapper.find('div').length).toBe(1)
})
test('handler multiple observers of props stream', () => {
const Other = () => <div />
const Div = componentFromStream(props$ =>
// Adds three observers to props stream
props$.combineLatest(props$, props$, props1 => <Other {...props1} />)
)
const wrapper = mount(<Div data-value={1} />)
const div = wrapper.find(Other)
expect(div.prop('data-value')).toBe(1)
wrapper.setProps({ 'data-value': 2 })
wrapper.update()
const div2 = wrapper.find(Other)
expect(div2.prop('data-value')).toBe(2)
})
test('complete props stream before unmounting', () => {
let counter = 0
const Div = componentFromStream(props$ => {
const first$ = props$.first().do(() => {
counter += 1
})
const last$ = props$
.last()
.do(() => {
counter -= 1
})
.startWith(null)
return props$.combineLatest(first$, last$, props1 => <div {...props1} />)
})
const wrapper = mount(<Div />)
expect(counter).toBe(1)
expect(wrapper.find('div').length).toBe(1)
wrapper.unmount()
expect(counter).toBe(0)
})
test('completed props stream should throw an exception', () => {
const Div = componentFromStream(props$ => {
const first$ = props$.filter(() => false).first().startWith(null)
return props$.combineLatest(first$, props1 => <div {...props1} />)
})
const wrapper = mount(<Div />)
expect(wrapper.find('div').length).toBe(1)
const error = sinon.stub(console, 'error')
expect(() => wrapper.unmount()).toThrowError(/no elements in sequence/)
expect(error.called).toBe(true)
})
================================================
FILE: src/packages/recompose/__tests__/componentFromStreamWithConfig-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import { Observable } from 'rxjs'
import { Stream as MostStream } from 'most'
import mostConfig from '../mostObservableConfig'
import rxjsConfig from '../rxjsObservableConfig'
import { componentFromStreamWithConfig } from '../componentFromStream'
test('componentFromStreamWithConfig creates a stream with the correct stream type.', () => {
const MostComponent = componentFromStreamWithConfig(mostConfig)(props$ => {
expect(props$ instanceof MostStream).toBe(true)
return props$.map(v =>
<div>
{String(v)}
</div>
)
})
mount(<MostComponent />)
const RXJSComponent = componentFromStreamWithConfig(rxjsConfig)(props$ => {
expect(props$ instanceof Observable).toBe(true)
return props$.map(v =>
<div>
{String(v)}
</div>
)
})
mount(<RXJSComponent />)
})
================================================
FILE: src/packages/recompose/__tests__/compose-test.js
================================================
import { compose } from '../'
test('compose composes from right to left', () => {
const double = x => x * 2
const square = x => x * x
expect(compose(square)(5)).toBe(25)
expect(compose(square, double)(5)).toBe(100)
expect(compose(double, square, double)(5)).toBe(200)
})
test('compose can be seeded with multiple arguments', () => {
const square = x => x * x
const add = (x, y) => x + y
expect(compose(square, add)(1, 2)).toBe(9)
})
test('compose returns the identity function if given no arguments', () => {
expect(compose()(1, 2)).toBe(1)
expect(compose()(3)).toBe(3)
expect(compose()()).toBe(undefined)
})
test('compose returns the first function if given only one', () => {
const fn = x => x * x
expect(compose(fn)(3)).toBe(fn(3))
})
================================================
FILE: src/packages/recompose/__tests__/createEventHandler-test.js
================================================
import { createEventHandler } from '../'
test('createEventHandler creates an event handler and a corresponding stream', () => {
const result = []
const { stream, handler } = createEventHandler()
const subscription = stream.subscribe({ next: v => result.push(v) })
handler(1)
handler(2)
handler(3)
subscription.unsubscribe()
expect(result).toEqual([1, 2, 3])
})
test('handles multiple subscribers', () => {
const result1 = []
const result2 = []
const { handler, stream } = createEventHandler()
const subscription1 = stream.subscribe({ next: v => result1.push(v) })
const subscription2 = stream.subscribe({ next: v => result2.push(v) })
handler(1)
handler(2)
handler(3)
subscription1.unsubscribe()
subscription2.unsubscribe()
expect(result1).toEqual([1, 2, 3])
expect(result2).toEqual([1, 2, 3])
})
================================================
FILE: src/packages/recompose/__tests__/createSink-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { createSink, compose, withState, mapProps } from '../'
test('createSink creates a React component that fires a callback when receiving new props', () => {
const spy = sinon.spy()
const Sink = createSink(spy)
const Counter = compose(
withState('counter', 'updateCounter', 0),
mapProps(({ updateCounter, ...rest }) => ({
increment: () => updateCounter(n => n + 1),
...rest,
}))
)(Sink)
mount(
<div>
<Counter />
</div>
)
const { increment } = spy.lastCall.args[0]
const getCounter = () => spy.lastCall.args[0].counter
expect(getCounter()).toBe(0)
increment()
expect(getCounter()).toBe(1)
increment()
expect(getCounter()).toBe(2)
})
================================================
FILE: src/packages/recompose/__tests__/defaultProps-test.js
================================================
import React from 'react'
import { shallow } from 'enzyme'
import { defaultProps } from '../'
test('defaultProps passes additional props to base component', () => {
const DoReMi = defaultProps({ 'data-so': 'do', 'data-la': 'fa' })('div')
expect(DoReMi.displayName).toBe('defaultProps(div)')
const div = shallow(<DoReMi />).find('div')
expect(div.equals(<div data-so="do" data-la="fa" />)).toBe(true)
})
test('defaultProps has lower precendence than props from owner', () => {
const DoReMi = defaultProps({ 'data-so': 'do', 'data-la': 'fa' })('div')
expect(DoReMi.displayName).toBe('defaultProps(div)')
const div = shallow(<DoReMi data-la="ti" />).find('div')
expect(div.equals(<div data-so="do" data-la="ti" />)).toBe(true)
})
test('defaultProps overrides undefined owner props', () => {
const DoReMi = defaultProps({ 'data-so': 'do', 'data-la': 'fa' })('div')
expect(DoReMi.displayName).toBe('defaultProps(div)')
const div = shallow(<DoReMi data-la={undefined} />).find('div')
expect(div.equals(<div data-so="do" data-la="fa" />)).toBe(true)
})
================================================
FILE: src/packages/recompose/__tests__/fixtures/treeshake-entry.js
================================================
import '../../../../../lib/packages/recompose/es/Recompose.js'
================================================
FILE: src/packages/recompose/__tests__/flattenProp-test.js
================================================
import React from 'react'
import { shallow } from 'enzyme'
import { flattenProp } from '../'
test('flattenProps flattens an object prop and spreads it into the top-level props object', () => {
const Counter = flattenProp('data-state')('div')
expect(Counter.displayName).toBe('flattenProp(div)')
const wrapper = shallow(
<Counter data-pass="through" data-state={{ 'data-counter': 1 }} />
)
expect(
wrapper.equals(
<div
data-pass="through"
data-state={{ 'data-counter': 1 }}
data-counter={1}
/>
)
).toBe(true)
wrapper.setProps({
'data-pass': 'through',
'data-state': { 'data-state': 1 },
})
expect(wrapper.equals(<div data-pass="through" data-state={1} />)).toBe(true)
})
================================================
FILE: src/packages/recompose/__tests__/fromRenderProps-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import { fromRenderProps, compose, toRenderProps, defaultProps } from '../'
test('fromRenderProps passes additional props to base component', () => {
const RenderPropsComponent = ({ children }) => children({ i18n: 'zh-TW' })
const EnhancedComponent = fromRenderProps(
RenderPropsComponent,
({ i18n }) => ({
i18n,
})
)('div')
expect(EnhancedComponent.displayName).toBe('fromRenderProps(div)')
const div = mount(<EnhancedComponent />)
expect(div.html()).toBe(`<div i18n="zh-TW"></div>`)
})
test('fromRenderProps passes additional props to base component with custom renderPropName', () => {
const RenderPropsComponent = ({ render }) => render({ i18n: 'zh-TW' })
const EnhancedComponent = fromRenderProps(
RenderPropsComponent,
({ i18n }) => ({
i18n,
}),
'render'
)('div')
expect(EnhancedComponent.displayName).toBe('fromRenderProps(div)')
const div = mount(<EnhancedComponent />)
expect(div.html()).toBe(`<div i18n="zh-TW"></div>`)
})
test('fromRenderProps passes additional props to base component with 2 RenderPropsComponents', () => {
const RenderPropsComponent1 = ({ children }) => children({ theme: 'dark' })
const RenderPropsComponent2 = ({ render }) => render({ i18n: 'zh-TW' })
const EnhancedComponent = compose(
fromRenderProps(
RenderPropsComponent1,
({ theme }) => ({ theme }),
'children'
),
fromRenderProps(
RenderPropsComponent2,
({ i18n }) => ({ locale: i18n }),
'render'
)
)('div')
expect(EnhancedComponent.displayName).toBe(
'fromRenderProps(fromRenderProps(div))'
)
const div = mount(<EnhancedComponent />)
expect(div.html()).toBe(`<div theme="dark" locale="zh-TW"></div>`)
})
test('fromRenderProps meet toRenderProps', () => {
const RenderPropsComponent = toRenderProps(
defaultProps({ foo1: 'bar1', foo2: 'bar2' })
)
const EnhancedComponent = fromRenderProps(
RenderPropsComponent,
({ foo1 }) => ({
foo: foo1,
})
)('div')
expect(EnhancedComponent.displayName).toBe('fromRenderProps(div)')
const div = mount(<EnhancedComponent />)
expect(div.html()).toBe(`<div foo="bar1"></div>`)
})
test('fromRenderProps with multiple arguments #693', () => {
const RenderPropsComponent = ({ children }) =>
children({ theme: 'dark' }, { data: 'data' })
const EnhancedComponent = compose(
fromRenderProps(
RenderPropsComponent,
({ theme }, { data }) => ({ theme, data }),
'children'
)
)('div')
expect(EnhancedComponent.displayName).toBe('fromRenderProps(div)')
const div = mount(<EnhancedComponent />)
expect(div.html()).toBe(`<div theme="dark" data="data"></div>`)
})
================================================
FILE: src/packages/recompose/__tests__/getContext-test.js
================================================
// Tests for getContext() are covered by withContext-test.js
// This is just a dummy test so Ava doesn't complain
test('getContext works', () => expect(true).toBe(true))
================================================
FILE: src/packages/recompose/__tests__/getDisplayName-test.js
================================================
import React from 'react'
import { getDisplayName } from '../'
test('getDisplayName gets the display name of a React component', () => {
class SomeComponent extends React.Component {
render() {
return <div />
}
}
class SomeOtherComponent extends React.Component {
static displayName = 'CustomDisplayName'
render() {
return <div />
}
}
function YetAnotherComponent() {
return <div />
}
expect(getDisplayName(SomeComponent)).toBe('SomeComponent')
expect(getDisplayName(SomeOtherComponent)).toBe('CustomDisplayName')
expect(getDisplayName(YetAnotherComponent)).toBe('YetAnotherComponent')
expect(getDisplayName(() => <div />)).toBe('Component')
expect(getDisplayName('div')).toBe('div')
})
================================================
FILE: src/packages/recompose/__tests__/hoistStatics-test.js
================================================
import React, { createFactory } from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { hoistStatics, mapProps } from '../'
test('copies non-React static properties from base component to new component', () => {
const BaseComponent = sinon.spy(() => null)
BaseComponent.foo = () => {}
const EnhancedComponent = hoistStatics(
mapProps(props => ({ n: props.n * 5 }))
)(BaseComponent)
expect(EnhancedComponent.foo).toBe(BaseComponent.foo)
mount(<EnhancedComponent n={3} />)
expect(BaseComponent.firstCall.args[0].n).toBe(15)
})
test('does not copy blacklisted static properties to new component ', () => {
const BaseComponent = sinon.spy(() => null)
BaseComponent.foo = () => {}
BaseComponent.bar = () => {}
const EnhancedComponent = hoistStatics(
comp => createFactory(comp),
{ bar: true } // Blacklist
)(BaseComponent)
expect(EnhancedComponent.foo).toBe(BaseComponent.foo)
expect(EnhancedComponent.bar).toBe(undefined)
})
================================================
FILE: src/packages/recompose/__tests__/isClassComponent-test.js
================================================
import React, { Component } from 'react'
import createReactClass from 'create-react-class'
import isClassComponent from '../isClassComponent'
test('isClassComponent returns false for functions', () => {
const Foo = () => <div />
expect(isClassComponent(Foo)).toBe(false)
})
test('isClassComponent returns true for React component classes', () => {
class Foo extends Component {
render() {
return <div />
}
}
/* eslint-disable react/prefer-es6-class */
const Bar = createReactClass({
render() {
return <div />
},
})
/* eslint-enable react/prefer-es6-class */
expect(isClassComponent(Foo)).toBe(true)
expect(isClassComponent(Bar)).toBe(true)
})
================================================
FILE: src/packages/recompose/__tests__/lifecycle-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import { lifecycle } from '../'
test('lifecycle is a higher-order component version of React.Component', () => {
const enhance = lifecycle({
componentWillMount() {
this.setState({ 'data-bar': 'baz' })
},
})
const Div = enhance('div')
expect(Div.displayName).toBe('lifecycle(div)')
const div = mount(<Div data-foo="bar" />).find('div')
expect(div.prop('data-foo')).toBe('bar')
expect(div.prop('data-bar')).toBe('baz')
})
================================================
FILE: src/packages/recompose/__tests__/mapProps-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { mapProps, withState, compose } from '../'
test('mapProps maps owner props to child props', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const StringConcat = compose(
withState('strings', 'updateStrings', ['do', 're', 'mi']),
mapProps(({ strings, ...rest }) => ({
...rest,
string: strings.join(''),
}))
)(component)
expect(StringConcat.displayName).toBe('withState(mapProps(component))')
mount(<StringConcat />)
const { updateStrings } = component.firstCall.args[0]
updateStrings(strings => [...strings, 'fa'])
expect(component.firstCall.args[0].string).toBe('doremi')
expect(component.secondCall.args[0].string).toBe('doremifa')
})
================================================
FILE: src/packages/recompose/__tests__/mapPropsStream-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import setObservableConfig from '../setObservableConfig'
import rxjs4Config from '../rxjs4ObservableConfig'
import { mapPropsStream } from '../'
setObservableConfig(rxjs4Config)
// Most of mapPropsStream's functionality is covered by componentFromStream
test('mapPropsStream creates a higher-order component from a stream', () => {
const Double = mapPropsStream(props$ =>
props$.map(({ n }) => ({ children: n * 2 }))
)('div')
const wrapper = mount(<Double n={112} />)
const div = wrapper.find('div')
expect(div.text()).toBe('224')
wrapper.setProps({ n: 358 })
expect(div.text()).toBe('716')
})
================================================
FILE: src/packages/recompose/__tests__/mapPropsStreamWithConfig-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import { Stream as MostStream } from 'most'
import { Observable } from 'rxjs'
import { mapPropsStreamWithConfig } from '../'
import rxConfig from '../rxjsObservableConfig'
import mostConfig from '../mostObservableConfig'
// Most of mapPropsStreamConfig's functionality is covered by componentFromStream
test('mapPropsStreamWithConfig creates a higher-order component from a stream and a observable config', () => {
const Double = mapPropsStreamWithConfig(rxConfig)(props$ =>
props$.map(({ n }) => ({ children: n * 2 }))
)('div')
const wrapper = mount(<Double n={112} />)
const div = wrapper.find('div')
expect(div.text()).toBe('224')
wrapper.setProps({ n: 358 })
expect(div.text()).toBe('716')
})
test('mapPropsStreamWithConfig creates a stream with the correct config', () => {
const MostComponent = mapPropsStreamWithConfig(mostConfig)(props$ => {
expect(props$ instanceof MostStream).toBe(true)
return props$.map(v => v)
})('div')
mount(<MostComponent />)
const RXJSComponent = mapPropsStreamWithConfig(rxConfig)(props$ => {
expect(props$ instanceof Observable).toBe(true)
return props$.map(v => v)
})('div')
mount(<RXJSComponent />)
})
================================================
FILE: src/packages/recompose/__tests__/nest-test.js
================================================
import React from 'react'
import { shallow } from 'enzyme'
import { nest, setDisplayName, toClass } from '../'
test('nest nests components from outer to inner', () => {
const A = setDisplayName('A')(toClass('div'))
const B = setDisplayName('B')(toClass('div'))
const C = setDisplayName('C')(toClass('div'))
const Nest = nest(A, B, C)
expect(Nest.displayName).toBe('nest(A, B, C)')
const wrapper = shallow(<Nest pass="through">Child</Nest>)
expect(
wrapper.equals(
<A pass="through">
<B pass="through">
<C pass="through">Child</C>
</B>
</A>
)
).toBe(true)
})
================================================
FILE: src/packages/recompose/__tests__/onlyUpdateForKeys-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { onlyUpdateForKeys, compose, withState } from '../'
test('onlyUpdateForKeys implements shouldComponentUpdate()', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = compose(
withState('counter', 'updateCounter', 0),
withState('foobar', 'updateFoobar', 'foobar'),
onlyUpdateForKeys(['counter'])
)(component)
expect(Counter.displayName).toBe(
'withState(withState(onlyUpdateForKeys(component)))'
)
mount(<Counter />)
const { updateCounter, updateFoobar } = component.firstCall.args[0]
expect(component.lastCall.args[0].counter).toBe(0)
expect(component.lastCall.args[0].foobar).toBe('foobar')
// Does not update
updateFoobar('barbaz')
expect(component.calledOnce).toBe(true)
updateCounter(42)
expect(component.calledTwice).toBe(true)
expect(component.lastCall.args[0].counter).toBe(42)
expect(component.lastCall.args[0].foobar).toBe('barbaz')
})
================================================
FILE: src/packages/recompose/__tests__/onlyUpdateForPropTypes-test.js
================================================
import React from 'react'
import PropTypes from 'prop-types'
import sinon from 'sinon'
import { mount, shallow } from 'enzyme'
import { onlyUpdateForPropTypes, compose, withState, setPropTypes } from '../'
test('onlyUpdateForPropTypes only updates for props specified in propTypes', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = compose(
withState('counter', 'updateCounter', 0),
withState('foobar', 'updateFoobar', 'foobar'),
onlyUpdateForPropTypes,
setPropTypes({ counter: PropTypes.number })
)(component)
expect(Counter.displayName).toBe(
'withState(withState(onlyUpdateForPropTypes(component)))'
)
mount(<Counter />)
const { updateCounter, updateFoobar } = component.firstCall.args[0]
expect(component.lastCall.args[0].counter).toBe(0)
expect(component.lastCall.args[0].foobar).toBe('foobar')
// Does not update
updateFoobar('barbaz')
expect(component.calledOnce).toBe(true)
updateCounter(42)
expect(component.calledTwice).toBe(true)
expect(component.lastCall.args[0].counter).toBe(42)
expect(component.lastCall.args[0].foobar).toBe('barbaz')
})
test('onlyUpdateForPropTypes warns if BaseComponent does not have any propTypes', () => {
const error = sinon.stub(console, 'error')
const ShouldWarn = onlyUpdateForPropTypes('div')
shallow(<ShouldWarn />)
expect(error.firstCall.args[0]).toBe(
'A component without any `propTypes` was passed to ' +
'`onlyUpdateForPropTypes()`. Check the implementation of the component ' +
'with display name "div".'
)
/* eslint-disable */
console.error.restore()
/* eslint-enable */
})
================================================
FILE: src/packages/recompose/__tests__/pure-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { pure, compose, withState } from '../'
import { countRenders } from './utils'
test('pure implements shouldComponentUpdate() using shallowEqual()', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const initialTodos = ['eat', 'drink', 'sleep']
const Todos = compose(
withState('todos', 'updateTodos', initialTodos),
pure,
countRenders
)(component)
expect(Todos.displayName).toBe('withState(pure(countRenders(component)))')
mount(<Todos />)
const { updateTodos } = component.firstCall.args[0]
expect(component.lastCall.args[0].todos).toBe(initialTodos)
expect(component.lastCall.args[0].renderCount).toBe(1)
// Does not re-render
updateTodos(initialTodos)
expect(component.calledOnce).toBe(true)
updateTodos(todos => todos.slice(0, -1))
expect(component.calledTwice).toBe(true)
expect(component.lastCall.args[0].todos).toEqual(['eat', 'drink'])
expect(component.lastCall.args[0].renderCount).toBe(2)
})
================================================
FILE: src/packages/recompose/__tests__/renameProp-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import { withProps, renameProp, compose } from '../'
test('renameProp renames a single prop', () => {
const StringConcat = compose(
withProps({ 'data-so': 123, 'data-la': 456 }),
renameProp('data-so', 'data-do')
)('div')
expect(StringConcat.displayName).toBe('withProps(renameProp(div))')
const div = mount(<StringConcat />).find('div')
expect(div.props()).toEqual({ 'data-do': 123, 'data-la': 456 })
})
================================================
FILE: src/packages/recompose/__tests__/renameProps-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import { withProps, renameProps, compose } from '../'
test('renameProps renames props', () => {
const StringConcat = compose(
withProps({ 'data-so': 123, 'data-la': 456 }),
renameProps({ 'data-so': 'data-do', 'data-la': 'data-fa' })
)('div')
expect(StringConcat.displayName).toBe('withProps(renameProps(div))')
const div = mount(<StringConcat />).find('div')
expect(div.prop('data-do')).toBe(123)
expect(div.prop('data-fa')).toBe(456)
})
================================================
FILE: src/packages/recompose/__tests__/renderComponent-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { renderComponent, withState, compose, branch } from '../'
test('renderComponent always renders the given component', () => {
const componentA = sinon.spy(() => null)
const componentB = sinon.spy(() => null)
const Foobar = compose(
withState('flip', 'updateFlip', false),
branch(
props => props.flip,
renderComponent(componentA),
renderComponent(componentB)
)
)(null)
mount(<Foobar />)
const { updateFlip } = componentB.firstCall.args[0]
expect(componentB.calledOnce).toBe(true)
expect(componentA.notCalled).toBe(true)
updateFlip(true)
expect(componentB.calledOnce).toBe(true)
expect(componentA.calledOnce).toBe(true)
})
================================================
FILE: src/packages/recompose/__tests__/renderNothing-test.js
================================================
import React from 'react'
import { shallow } from 'enzyme'
import { renderNothing } from '../'
test('renderNothing returns a component that renders null', () => {
const Nothing = renderNothing('div')
const wrapper = shallow(<Nothing />)
const Parent = () => <Nothing />
const parentWrapper = shallow(<Parent />)
expect(wrapper.type()).toBe(null)
expect(parentWrapper.text()).toBe('<Nothing />')
})
================================================
FILE: src/packages/recompose/__tests__/setDisplayName-test.js
================================================
import React from 'react'
import { setDisplayName } from '../'
test('setDisplayName sets a static property on the base component', () => {
const BaseComponent = () => <div />
const NewComponent = setDisplayName('Foo')(BaseComponent)
expect(NewComponent.displayName).toBe('Foo')
})
================================================
FILE: src/packages/recompose/__tests__/setObservableConfig-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import rxjs5Config from '../rxjsObservableConfig'
import rxjs4Config from '../rxjs4ObservableConfig'
import mostConfig from '../mostObservableConfig'
import xstreamConfig from '../xstreamObservableConfig'
import baconConfig from '../baconObservableConfig'
import kefirConfig from '../kefirObservableConfig'
import flydConfig from '../flydObservableConfig'
import setObservableConfig from '../setObservableConfig'
import componentFromStream from '../componentFromStream'
const testTransform = transform => {
const Double = componentFromStream(transform)
const wrapper = mount(<Double n={112} />)
const div = wrapper.find('div')
expect(div.text()).toBe('224')
wrapper.setProps({ n: 358 })
expect(div.text()).toBe('716')
}
test('works with RxJS 5', () => {
setObservableConfig(rxjs5Config)
testTransform(props$ =>
props$.map(({ n }) =>
<div>
{n * 2}
</div>
)
)
})
test('works with RxJS 4', () => {
setObservableConfig(rxjs4Config)
testTransform(props$ =>
props$.map(({ n }) =>
<div>
{n * 2}
</div>
)
)
})
test('works with most', () => {
setObservableConfig(mostConfig)
testTransform(props$ =>
props$.map(({ n }) =>
<div>
{n * 2}
</div>
)
)
})
test('works with xstream', () => {
setObservableConfig(xstreamConfig)
testTransform(props$ =>
props$.map(({ n }) =>
<div>
{n * 2}
</div>
)
)
})
test('works with bacon', () => {
setObservableConfig(baconConfig)
testTransform(props$ =>
props$.map(({ n }) =>
<div>
{n * 2}
</div>
)
)
})
test('works with kefir', () => {
setObservableConfig(kefirConfig)
testTransform(props$ =>
props$.map(({ n }) =>
<div>
{n * 2}
</div>
)
)
})
test('works with flyd', () => {
setObservableConfig(flydConfig)
testTransform(props$ =>
props$.map(({ n }) =>
<div>
{n * 2}
</div>
)
)
})
================================================
FILE: src/packages/recompose/__tests__/setPropTypes-test.js
================================================
import React from 'react'
import PropTypes from 'prop-types'
import { setPropTypes } from '../'
test('setPropTypes sets a static property on the base component', () => {
const BaseComponent = () => <div />
const NewComponent = setPropTypes({ foo: PropTypes.object })(BaseComponent)
expect(NewComponent.propTypes).toEqual({
foo: PropTypes.object,
})
})
================================================
FILE: src/packages/recompose/__tests__/setStatic-test.js
================================================
import React from 'react'
import PropTypes from 'prop-types'
import { setStatic } from '../'
test('setStatic sets a static property on the base component', () => {
const BaseComponent = () => <div />
const NewComponent = setStatic('propTypes', { foo: PropTypes.object })(
BaseComponent
)
expect(NewComponent.propTypes).toEqual({
foo: PropTypes.object,
})
})
================================================
FILE: src/packages/recompose/__tests__/shallowEqual-test.js
================================================
import { shallowEqual } from '../'
// Adapted from https://github.com/rackt/react-redux/blob/master/test/utils/shallowEqual.spec.js
test('shallowEqual returns true if arguments are equal, without comparing properties', () => {
const throwOnAccess = {
get foo() {
throw new Error('Property was accessed')
},
}
expect(shallowEqual(throwOnAccess, throwOnAccess)).toBe(true)
})
test('shallowEqual returns true if arguments fields are equal', () => {
expect(
shallowEqual({ a: 1, b: 2, c: undefined }, { a: 1, b: 2, c: undefined })
).toBe(true)
expect(shallowEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).toBe(true)
const o = {}
expect(shallowEqual({ a: 1, b: 2, c: o }, { a: 1, b: 2, c: o })).toBe(true)
})
test('shallowEqual returns false if either argument is null or undefined', () => {
expect(shallowEqual(null, { a: 1, b: 2 })).toBe(false)
expect(shallowEqual({ a: 1, b: 2 }, null)).toBe(false)
})
test('shallowEqual returns false if first argument has too many keys', () => {
expect(shallowEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 })).toBe(false)
})
test('shallowEqual returns false if second argument has too many keys', () => {
expect(shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2, c: 3 })).toBe(false)
})
test('shallowEqual returns false if arguments have different keys', () => {
expect(
shallowEqual({ a: 1, b: 2, c: undefined }, { a: 1, bb: 2, c: undefined })
).toBe(false)
})
================================================
FILE: src/packages/recompose/__tests__/shouldUpdate-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { shouldUpdate, compose, withState } from '../'
import { countRenders } from './utils'
test('shouldUpdate implements shouldComponentUpdate', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const initialTodos = ['eat', 'drink', 'sleep']
const Todos = compose(
withState('todos', 'updateTodos', initialTodos),
shouldUpdate((props, nextProps) => props.todos !== nextProps.todos),
countRenders
)(component)
expect(Todos.displayName).toBe(
'withState(shouldUpdate(countRenders(component)))'
)
mount(<Todos />)
const { updateTodos } = component.firstCall.args[0]
expect(component.lastCall.args[0].todos).toBe(initialTodos)
expect(component.lastCall.args[0].renderCount).toBe(1)
// Does not re-render
updateTodos(initialTodos)
expect(component.calledOnce).toBe(true)
updateTodos(todos => todos.slice(0, -1))
expect(component.calledTwice).toBe(true)
expect(component.lastCall.args[0].todos).toEqual(['eat', 'drink'])
expect(component.lastCall.args[0].renderCount).toBe(2)
})
================================================
FILE: src/packages/recompose/__tests__/toClass-test.js
================================================
import React from 'react'
import PropTypes from 'prop-types'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { toClass, withContext, compose } from '../'
test('toClass returns the base component if it is already a class', () => {
class BaseComponent extends React.Component {
render() {
return <div />
}
}
const TestComponent = toClass(BaseComponent)
expect(TestComponent).toBe(BaseComponent)
})
test('toClass copies propTypes, displayName, contextTypes and defaultProps from base component', () => {
const StatelessComponent = () => <div />
StatelessComponent.displayName = 'Stateless'
StatelessComponent.propTypes = { foo: PropTypes.string }
StatelessComponent.contextTypes = { bar: PropTypes.object }
StatelessComponent.defaultProps = { foo: 'bar', fizz: 'buzz' }
const TestComponent = toClass(StatelessComponent)
expect(TestComponent.displayName).toBe('Stateless')
expect(TestComponent.propTypes).toEqual({ foo: PropTypes.string })
expect(TestComponent.contextTypes).toEqual({ bar: PropTypes.object })
expect(TestComponent.defaultProps).toEqual({ foo: 'bar', fizz: 'buzz' })
})
test('toClass passes defaultProps correctly', () => {
const StatelessComponent = sinon.spy(() => null)
StatelessComponent.displayName = 'Stateless'
StatelessComponent.propTypes = { foo: PropTypes.string }
StatelessComponent.contextTypes = { bar: PropTypes.object }
StatelessComponent.defaultProps = { foo: 'bar', fizz: 'buzz' }
const TestComponent = toClass(StatelessComponent)
mount(<TestComponent />)
expect(StatelessComponent.lastCall.args[0].foo).toBe('bar')
expect(StatelessComponent.lastCall.args[0].fizz).toBe('buzz')
})
test('toClass passes context and props correctly', () => {
const store = {}
class Provider extends React.Component {
static propTypes = {
children: PropTypes.node,
}
render() {
return this.props.children
}
}
Provider = compose(
withContext({ store: PropTypes.object }, props => ({ store: props.store }))
)(Provider)
const StatelessComponent = (props, context) =>
<div data-props={props} data-context={context} />
StatelessComponent.contextTypes = { store: PropTypes.object }
const TestComponent = toClass(StatelessComponent)
const div = mount(
<Provider store={store}>
<TestComponent fizz="fizzbuzz" />
</Provider>
).find('div')
expect(div.prop('data-props').fizz).toBe('fizzbuzz')
expect(div.prop('data-context').store).toBe(store)
})
test('toClass works with strings (DOM components)', () => {
const Div = toClass('div')
const div = mount(<Div>Hello</Div>)
expect(div.html()).toBe('<div>Hello</div>')
})
================================================
FILE: src/packages/recompose/__tests__/toRenderProps-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import { toRenderProps, defaultProps } from '../'
test('toRenderProps creates a component from defaultProps HOC', () => {
const enhance = defaultProps({ foo: 'bar' })
const Enhanced = toRenderProps(enhance)
expect(Enhanced.displayName).toBe('defaultProps(RenderPropsComponent)')
const h1 = mount(
<Enhanced>
{({ foo }) =>
<h1>
{foo}
</h1>}
</Enhanced>
).find('h1')
expect(h1.html()).toBe(`<h1>bar</h1>`)
})
================================================
FILE: src/packages/recompose/__tests__/types/test_branch.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import {
compose,
withProps,
branch,
renderNothing,
renderComponent,
onlyUpdateForKeys,
} from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ eA }) =>
<div>
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
branch(({ eA }) => eA === 1, renderNothing),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const enhacerLoading: HOC<*, EnhancedCompProps> = compose(
branch(({ eA }) => eA === 1, renderComponent(p => <div>Loading</div>)),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
}))
)
// can work with onlyUpdateForKeys
const enhacerUpdating: HOC<*, EnhancedCompProps> = compose(
branch(({ eA }) => eA === 1, onlyUpdateForKeys(['eA'])),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
}))
)
// can infer withProps type
const enhacerWithProps: HOC<*, EnhancedCompProps> = compose(
branch(({ eA }) => eA === 1, withProps(props => ({ x: 1 }))),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
}))
)
// can infer compose types
const enhacerWithCompose: HOC<*, EnhancedCompProps> = compose(
branch(
({ eA }) => eA === 1,
compose(
withProps(props => {
// $ExpectError eA nor any nor string
;(props.eA: string)
return { x: 1 }
}),
withProps(props => ({ y: 2 }))
)
),
withProps(props => ({
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
// $ExpectError x nor any nor string
xErr: (props.x: string),
// $ExpectError y nor any nor string
yErr: (props.y: string),
}))
)
const enhacerLeftRight: HOC<*, EnhancedCompProps> = compose(
branch(
({ eA }) => eA === 1,
renderComponent(p => <div>A</div>),
renderComponent(p => <div>B</div>)
),
withProps(props => ({
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
// $ExpectError x nor any nor string
xErr: (props.x: string),
// $ExpectError y nor any nor string
yErr: (props.y: string),
}))
)
/*
Wrong types of left right, this will cause an infinite recursion
const enhacerLeftRight2: HOC<*, EnhancedCompProps> = compose(
branch(
({ eA }) => eA === 1,
renderComponent(p => <div>A</div>),
withProps(props => ({ y: 2 }))
)
);
*/
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_classBasedEnhancer.js
================================================
/* @flow */
import * as React from 'react'
import { compose, withProps } from '../..'
import type { HOC } from '../..'
// Example of very dirty written fetcher enhancer
function fetcher<Response: {}, Base: {}>(
dest: string,
nullRespType: ?Response
): HOC<{ ...$Exact<Base>, data?: Response }, Base> {
return BaseComponent =>
class Fetcher extends React.Component<Base, { data?: Response }> {
state = { data: undefined }
componentDidMount() {
fetch(dest)
.then(r => r.json())
.then((data: Response) => this.setState({ data }))
}
render() {
return <BaseComponent {...this.props} {...this.state} />
}
}
}
// Enhanced Component props type
type EnhancedCompProps = { b: number }
// response type
type FetchResponseType = { hello: string, world: number }
// Now you can use it, let's check
const enhancer: HOC<*, EnhancedCompProps> = compose(
// pass response type via typed null
fetcher('http://endpoint.ep', (null: ?FetchResponseType)),
// see here fully typed data
withProps(({ data }) => {
if (data !== undefined) {
return {
h: (data.hello: string),
// $ExpectError
hE: (data.hello: number),
}
}
return {}
})
)
================================================
FILE: src/packages/recompose/__tests__/types/test_componentFromStream.js
================================================
// @flow
import React from 'react'
import { componentFromStream } from '../..'
// $ExpectError
componentFromStream(1)
// $ExpectError
const result1: number = componentFromStream(() => React.createElement('div'))
componentFromStream(a => a)
================================================
FILE: src/packages/recompose/__tests__/types/test_createEventHandler.js
================================================
// @flow
import React from 'react'
import { createEventHandler } from '../..'
// $ExpectError
createEventHandler(1)
// $ExpectError
const result1: number = createEventHandler()
// $ExpectError
const { stream1, handler1 } = createEventHandler()
const { stream, handler } = createEventHandler()
handler()
================================================
FILE: src/packages/recompose/__tests__/types/test_defaultProps.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, defaultProps } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ hello, eA }) =>
<div>
{(hello: string)}
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
{
// $ExpectError hello nor any nor number
(hello: number)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
defaultProps({
hello: 'world',
}),
withProps(props => ({
hello: (props.hello: string),
eA: (props.eA: number),
// $ExpectError hello nor any nor number
helloErr: (props.hello: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_fromRenderProps.js
================================================
/* @flow */
import React from 'react'
import { compose, fromRenderProps } from '../..'
import type { HOC } from '../..'
const RenderPropsComponent1 = ({ children }) => children({ theme: 'dark' })
const RenderPropsComponent2 = ({ render }) => render({ i18n: 'zh-TW' })
const RenderPropsComponent3 = ({ children }) =>
children({ theme: 'dark' }, { data: 'data' })
type EnhancedCompProps = {||}
const Comp = ({ i18n, theme, data }) =>
<div>
{i18n}
{theme}
{data}
{
// $ExpectError
(i18n: number)
}
{
// $ExpectError
(theme: number)
}
{
// $ExpectError
(data: number)
}
</div>
const enhancer: HOC<*, EnhancedCompProps> = compose(
fromRenderProps(RenderPropsComponent1, props => ({
theme: props.theme,
// $ExpectError property not found
err: props.iMNotExists,
})),
fromRenderProps(
RenderPropsComponent2,
props => ({
i18n: props.i18n,
// $ExpectError property not found
err: props.iMNotExists,
}),
'render'
),
fromRenderProps(RenderPropsComponent3, (props, data) => ({
theme: props.theme,
data: data.data,
// $ExpectError property not found
err: data.iMNotExists,
}))
)
const EnhancedComponent = enhancer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_functionalEnhancer.js
================================================
/* @flow */
import * as React from 'react'
import { compose, withProps } from '../..'
import type { HOC } from '../..'
function mapProps<BaseProps: {}, EnhancedProps>(
mapperFn: EnhancedProps => BaseProps
): (React.ComponentType<BaseProps>) => React.ComponentType<EnhancedProps> {
return Component => props => <Component {...mapperFn(props)} />
}
type EnhancedProps = { hello: string }
const baseComponent = ({ hello, len }) =>
<div>
{(hello: string)}
{
// $ExpectError
(hello: number)
}
{(len: number)}
{
// $ExpectError
(len: string)
}
</div>
const enhancer: HOC<*, EnhancedProps> = compose(
mapProps(({ hello }) => ({
hello: `${hello} world`,
len: hello.length,
})),
withProps(props => ({
helloAndLen: `${props.hello} ${props.len}`,
// $ExpectError
lE: (props.len: string),
}))
)
enhancer(baseComponent)
================================================
FILE: src/packages/recompose/__tests__/types/test_getContext.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, withProps, getContext } from '../..'
// import PropTypes from 'prop-types'
import type { HOC } from '../..'
const PropTypes = {
number: () => {},
string: () => {},
}
type EnhancedCompProps = { eA: 1 }
const Comp = ({ eA }) =>
<div>
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
getContext({
// as an idea is to use a hack like this
// so we can test all such types
color: ((PropTypes.string: any): string),
num: ((PropTypes.number: any): number),
}),
withProps(props => ({
eA: (props.eA: number),
color: (props.color: string),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
// $ExpectError color nor any nor number
colorErr: (props.color: number),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_mapProps.js
================================================
/* globals */
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, mapProps, withProps } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ a }) =>
<div>
{(a: string)}
{
// $ExpectError
(a: number)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
mapProps(p => ({
a: '1',
})),
// If you need to to detect erros after a mapProps HOC
// you need to explicitly set Types for all HOCs below
// seems like this https://github.com/facebook/flow/issues/4342 issue
withProps(props => ({
a: (props.a: string),
// $ExpectError but not
e: Math.round(props.a),
}))
)
enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_mapPropsStream.js
================================================
/* globals */
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
// import { Observable } from 'rxjs'
import { compose, mapProps, withProps, mapPropsStream } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Observable = {
of: (a: Object) => Object,
}
const Comp = ({ a }) =>
<div>
{(a: string)}
{
// $ExpectError
(a: number)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
(mapPropsStream((props$: Observable<EnhancedCompProps>) =>
Observable.of({ a: 1, b: '1' })
): HOC<{ a: string, b: string }, *>),
// If you need to to detect erros after a mapPropsStream HOC (the same for mapProps and some others)
// you need to explicitly set Types for all HOCs below
// but because of this https://github.com/facebook/flow/issues/4342
withProps(props => ({
a: (props.a: string),
// $ExpectError but not
e: Math.round(props.a),
}))
)
enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_onlyUpdateForKeys.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, onlyUpdateForKeys } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ eA }) =>
<div>
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
onlyUpdateForKeys(['eA']),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const enhacerErr: HOC<*, EnhancedCompProps> = compose(
// $ExpectError property not found
onlyUpdateForKeys(['eB'])
)
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_onlyUpdateForPropTypes.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, onlyUpdateForPropTypes } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ eA }) =>
<div>
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
onlyUpdateForPropTypes,
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_pure.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, pure } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ eA }) =>
<div>
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
pure,
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_shouldUpdate.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, withProps, shouldUpdate } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ eA }) =>
<div>
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
shouldUpdate((props, nextProps) => {
// $ExpectError eA nor any nor string
;(props.eA: string)
// $ExpectError eA nor any nor string
;(nextProps.eA: string)
return props.eA === nextProps.eA
}),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const enhacerErr: HOC<*, EnhancedCompProps> = compose(
shouldUpdate(() => {
// $ExpectError must be boolean
return 1
})
)
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_statics.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import {
compose,
withProps,
setStatic,
setPropTypes,
setDisplayName,
} from '../..'
// import PropTypes from 'prop-types'
import type { HOC } from '../..'
const PropTypes = {
string: () => {},
}
type EnhancedCompProps = { eA: 1 }
const Comp = ({ eA }) =>
<div>
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
setStatic('hello', 'world'),
setPropTypes({
a: PropTypes.string,
}),
setDisplayName('hello'),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
// $ExpectError name is string
setDisplayName(1)
// $ExpectError propTypes is object
setPropTypes(1)
// $ExpectError name is string
setStatic(1, 'world')
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_toClass.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, toClass } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ eA }) =>
<div>
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
toClass,
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_toRenderProps.js
================================================
/* @flow */
import * as React from 'react'
import { compose, withProps, toRenderProps, withHandlers } from '../..'
import type { HOC } from '../..'
const enhance: HOC<*, {| +x: number |}> = compose(
withProps(props => ({
y: props.x + 1,
})),
withHandlers({
sayHello: ({ y }) => () => {
console.log('Hello', y)
},
})
)
const WithProps = toRenderProps(enhance)
const Comp = () =>
<WithProps x={1}>
{({ y, sayHello }) =>
<div onClick={() => sayHello()}>
{y}
</div>}
</WithProps>
const Comp2 = () =>
// $ExpectError
<WithProps x={'1'}>
{({ y, sayHello }) =>
<div onClick={() => sayHello()}>
{y}
</div>}
</WithProps>
// $ExpectError cannot create `WithProps` element because property `children` is missing in props
const Comp3 = () => <WithProps x={1} />
const Comp4 = () =>
<WithProps x={1}>
{({ y, sayHello }) =>
<div
onClick={() => {
;(sayHello: () => void)
// $ExpectError
;(sayHello: number)
sayHello()
}}
>
{(y: number)}
{
// $ExpectError
(y: string)
}
</div>}
</WithProps>
================================================
FILE: src/packages/recompose/__tests__/types/test_utils.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, withProps, hoistStatics } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { a: number }
const A = ({ a, b }) =>
<div>
{a}
{(b: string)}
{
// $ExpectError
(a: string)
}
{
// $ExpectError
(b: number)
}
</div>
A.displayName = 'HELLO WORLD'
const enhacer: HOC<*, EnhancedCompProps> = compose(
withProps(({ a }) => ({
hello: a,
b: `${a}`,
}))
)
hoistStatics(enhacer)(A)
// I see no reason to test other utils, please add if you think otherwise
================================================
FILE: src/packages/recompose/__tests__/types/test_voodoo.js
================================================
/* globals $Exact, $PropertyType */
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import {
compose,
withProps,
flattenProp,
renameProp,
renameProps,
withState,
} from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = {
eA: number,
obj: { objPropA: string, objPropB: number },
}
const Comp = ({ eA, objPropA }) =>
<div>
{(eA: number)}
{(objPropA: string)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
{
// $ExpectError eA nor any nor string
(objPropA: number)
}
</div>
const Comp2 = ({ eA, objPropA }) =>
<div>
{/* hack to preview types */}
{/* :: eA, objPropA */}
</div>
const flattenEnhacer: HOC<*, EnhancedCompProps> = compose(
(flattenProp('obj'): HOC<
{
...$Exact<EnhancedCompProps>,
...$Exact<$PropertyType<EnhancedCompProps, 'obj'>>,
},
EnhancedCompProps
>),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError
eB: (props.eA: string),
}))
)
const EnhancedComponent = flattenEnhacer(Comp)
const EnhancedComponent2 = flattenEnhacer(Comp2)
// renameEnhacer voodoo (you don't need it, use withProps instead)
const RenameComp = ({ eA, objNew, obj }) =>
<div>
{(eA: number)}
{
// objNew has a type we need
(objNew.objPropA: string)
}
{
// $ExpectError eA nor any nor string
(eA: string)
}
{
// $ExpectError eA nor any nor string
(objNew.objPropA: number)
}
{
// obj is null
(obj: null)
}
{
// $ExpectError eA nor any nor string
(obj: string)
}
</div>
const renameEnhacer: HOC<*, EnhancedCompProps> = compose(
(renameProp('obj', 'objNew'): HOC<
{
...$Exact<EnhancedCompProps>,
...$Exact<{ obj: null }>,
// $PropertyType does not work here
...$Exact<{ objNew: { objPropA: string, objPropB: number } }>,
},
EnhancedCompProps
>),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError
eB: (props.eA: string),
}))
)
renameEnhacer(RenameComp)
const renamePropsEnhacer: HOC<*, EnhancedCompProps> = compose(
(renameProps({ obj: 'objNew' }): HOC<
{
...$Exact<EnhancedCompProps>,
// --- repeat for every key ---
...$Exact<{ obj: null }>,
// $PropertyType does not work here
...$Exact<{ objNew: { objPropA: string, objPropB: number } }>,
},
EnhancedCompProps
>),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError
eB: (props.eA: string),
}))
)
// use withStateHandlers instead
const withStateEnhancer: HOC<*, EnhancedCompProps> = compose(
(withState('a', 'setA', { hello: 'world' }): HOC<
{
...$Exact<EnhancedCompProps>,
...$Exact<{ a: { hello: string }, setA: (a: { hello: string }) => void }>,
},
EnhancedCompProps
>),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError
eB: (props.eA: string),
}))
)
// withReducer see withState above
// lifecycle see withState above
================================================
FILE: src/packages/recompose/__tests__/types/test_withContext.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, withProps, withContext } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ eA }) =>
<div>
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
withContext({}, props => {
// $ExpectError eA nor any nor string
;(props.eA: string)
return {}
}),
withProps(props => ({
eA: (props.eA: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_withHandlers.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, withHandlers } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = {
value: number,
onChange: (value: number) => void,
onOtherChange: (value: { id: string }) => void,
}
const enhancer: HOC<*, EnhancedCompProps> = compose(
withHandlers({
onValueChange: props => value => {
props.onChange(value)
return true
},
onOtherValueChange: props => value => {
props.onOtherChange(value)
return true
},
}),
// here props itself will not be infered without explicit handler args types
withProps(props => ({
valueClone: (props.value: number),
resType: (props.onValueChange(0): boolean),
ee: props.onOtherValueChange({ id: 'aa' }),
// $ExpectError result is not any or number
resTypeErr: (props.onValueChange(0): number),
// $ExpectError property not found
err: props.iMNotExists,
}))
)
// check that factory init works as expected
const enhancer2: HOC<*, EnhancedCompProps> = compose(
withHandlers(() => ({
onValueChange: props => value => {
props.onChange(value)
return true
},
})),
// here props itself will not be infered without explicit handler args types
withProps(props => ({
valueClone: (props.value: number),
resType: (props.onValueChange(0): boolean),
// $ExpectError result is not any or number
resTypeErr: (props.onValueChange(0): number),
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const BaseComp = ({ value, onValueChange }) =>
<div
onClick={() => {
const res = onValueChange(1)
;(res: boolean)
// $ExpectError
;(res: number)
}}
>
{(value: number)}
{
// $ExpectError value is not any or string
(value: string)
}
</div>
const Enhanced = enhancer(BaseComp)
================================================
FILE: src/packages/recompose/__tests__/types/test_withProps.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { a: string, b: number }
const Comp = ({ hello, b }) =>
<div>
{hello}
{b}
{
// $ExpectError
(b: number)
}
{
// $ExpectError
(hello: number)
}
</div>
const enhancer: HOC<*, EnhancedCompProps> = compose(
withProps(({ a, b }) => ({
hello: a,
b: `${b}`,
})),
withProps(({ b, hello }) => ({
hello: (hello: string),
// $ExpectError (This type is incompatible with number)
c: (b: number),
})),
// check non functional form of with props
withProps({
d: 'hi',
}),
withProps(props => ({
a: (props.a: string),
d: (props.d: string),
// $ExpectError property not found
err: props.iMNotExists,
// $ExpectError a not a number and not any
aErr: (props.a: number),
// $ExpectError d not a number and not any
dErr: (props.d: number),
}))
)
const EnhancedComponent = enhancer(Comp)
;<EnhancedComponent a={'1'} b={1} />
// $ExpectError
;<EnhancedComponent a={'1'} b={'1'} />
// $ExpectError
;<EnhancedComponent a={'1'} />
================================================
FILE: src/packages/recompose/__tests__/types/test_withPropsOnChange.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */
/* @flow */
import React from 'react'
import { compose, withProps, withPropsOnChange } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = { eA: 1 }
const Comp = ({ hello, eA }) =>
<div>
{(hello: string)}
{(eA: number)}
{
// $ExpectError eA nor any nor string
(eA: string)
}
{
// $ExpectError hello nor any nor number
(hello: number)
}
</div>
const enhacer: HOC<*, EnhancedCompProps> = compose(
withPropsOnChange(['eA'], ({ eA }) => ({
hello: `${eA}`,
})),
withProps(props => ({
hello: (props.hello: string),
eA: (props.eA: number),
// $ExpectError hello nor any nor number
helloErr: (props.hello: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
})),
withProps(props => ({
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const enhacerFn: HOC<*, EnhancedCompProps> = compose(
withPropsOnChange(
(props, nextProps) => {
;(props.eA: number)
;(nextProps.eA: number)
// $ExpectError eA nor any nor string
;(props.eA: string)
// $ExpectError eA nor any nor string
;(nextProps.eA: string)
return props.eA === props.eA
},
({ eA }) => ({
hello: `${eA}`,
})
),
withProps(props => ({
hello: (props.hello: string),
eA: (props.eA: number),
// $ExpectError hello nor any nor number
helloErr: (props.hello: number),
// $ExpectError eA nor any nor string
eAErr: (props.eA: string),
}))
)
const enhacerErr: HOC<*, EnhancedCompProps> = compose(
// $ExpectError property property `eB` not found
withPropsOnChange(['eA', 'eB'], ({ eA }) => ({
hello: `${eA}`,
}))
)
const enhacerFnErr: HOC<*, EnhancedCompProps> = compose(
withPropsOnChange(
(props, nextProps) => {
// $ExpectError boolean
return 1
},
({ eA }) => ({
hello: `${eA}`,
})
),
withProps(props => ({
hello: (props.hello: string),
eA: (props.eA: number),
}))
)
const EnhancedComponent = enhacer(Comp)
================================================
FILE: src/packages/recompose/__tests__/types/test_withStateHandlers.js
================================================
/* eslint-disable no-unused-vars, no-unused-expressions */
/* @flow */
import React from 'react'
import { compose, withProps, withStateHandlers } from '../..'
import type { HOC } from '../..'
type EnhancedCompProps = {
initialCounter: number,
}
const enhancer: HOC<*, EnhancedCompProps> = compose(
withStateHandlers(
{ value: 'Hello', letIt: 'be', obj: ({}: { [key: string]: string }) },
{
// we need to set argument type so inference will work good
setValue: (state, props) => (value: string) => ({
value,
}),
changeValue: (state, props) => (
{ i, j }: { i: number, j: string },
k: number
) => ({
value: `world again ${i} ${j}`,
}),
inform: state => () => {},
}
),
// here props itself will not be infered without explicit handler args types
withProps(props => ({
hi: (props.value: string),
ic: (props.initialCounter: number),
cc: (props.obj.a: string),
// $ExpectError value not a number or any
ehi: (props.value: number),
// $ExpectError not a number
cn: (props.obj.a: number),
// $ExpectError property not found (to detect that props is not any)
err: props.iMNotExists,
// $ExpectError initialCounter not any nor string
icErr: (props.initialCounter: string),
}))
)
const enhancerFuncInit: HOC<*, EnhancedCompProps> = compose(
withStateHandlers(
props => ({
counter: props.initialCounter,
}),
{
// it's better to set argument type with named props, easier to find an error
// if you call it with wrong arguments
incCounter: ({ counter }) => ({ value }: { value: number }) => ({
counter: counter + value,
}),
}
),
withProps(props => ({
// check that result is void
iVal: (props.incCounter({ value: 1 }): void),
// $ExpectError check that incCounter is not any
iVal2: (props.incCounter({ value: 1 }): number),
// $ExpectError property not found
err: props.iMNotExists,
}))
)
const BaseComponent = ({ hi, changeValue, setValue }) =>
<div
onClick={() => {
// check that supports few arguments
const x = changeValue({ i: 1, j: '1' }, 1)
setValue('ww')
// Check that result is void
;(x: void)
// $ExpectError check that x is not any
;(x: {})
// Check hi
;(hi: string)
// $ExpectError check that hi is not any
;(hi: number)
}}
>
{hi}
</div>
const EnhancedComponent = enhancer(BaseComponent)
;<EnhancedComponent initialCounter={0} />
// Without $Exact<State> this will cause error
const enhancer3: HOC<*, EnhancedCompProps> = compose(
withStateHandlers(
({
mapA2B: {},
}: { mapA2B: { [key: string]: string } }),
{}
),
withProps(props => ({
// check that result is void
iVal: props.mapA2B.c,
}))
)
================================================
FILE: src/packages/recompose/__tests__/utils.js
================================================
import React from 'react'
import setDisplayName from '../setDisplayName'
import wrapDisplayName from '../wrapDisplayName'
export const countRenders = BaseComponent => {
class CountRenders extends React.Component {
renderCount = 0
render() {
this.renderCount += 1
return <BaseComponent renderCount={this.renderCount} {...this.props} />
}
}
return setDisplayName(wrapDisplayName(BaseComponent, 'countRenders'))(
CountRenders
)
}
================================================
FILE: src/packages/recompose/__tests__/withContext-test.js
================================================
/* eslint-disable react/require-default-props */
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { withContext, getContext, compose, mapProps } from '../'
test('withContext + getContext adds to and grabs from context', () => {
// Mini React Redux clone
const store = {
getState: () => ({
todos: ['eat', 'drink', 'sleep'],
counter: 12,
}),
}
class BaseProvider extends Component {
static propTypes = {
children: PropTypes.node,
}
render() {
return this.props.children
}
}
const Provider = compose(
withContext({ store: PropTypes.object }, props => ({ store: props.store }))
)(BaseProvider)
expect(Provider.displayName).toBe('withContext(BaseProvider)')
const connect = selector =>
compose(
getContext({ store: PropTypes.object }),
mapProps(props => selector(props.store.getState()))
)
const component = sinon.spy(() => null)
component.displayName = 'component'
const TodoList = connect(({ todos }) => ({ todos }))(component)
expect(TodoList.displayName).toBe('getContext(mapProps(component))')
mount(
<Provider store={store}>
<TodoList />
</Provider>
)
expect(component.lastCall.args[0].todos).toEqual(['eat', 'drink', 'sleep'])
})
================================================
FILE: src/packages/recompose/__tests__/withHandlers-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { withHandlers, withState, compose } from '../'
test('withHandlers passes handlers to base component', () => {
let submittedFormValue
const enhanceForm = compose(
withState('value', 'updateValue', ''),
withHandlers({
onChange: props => event => {
props.updateValue(event.target.value)
},
onSubmit: props => () => {
submittedFormValue = props.value
},
})
)
const Form = enhanceForm(({ value, onChange, onSubmit }) =>
<form onSubmit={onSubmit}>
<label>
Value
<input type="text" value={value} onChange={onChange} />
</label>
<p>
{value}
</p>
</form>
)
const wrapper = mount(<Form />)
const input = wrapper.find('input')
const output = wrapper.find('p')
const form = wrapper.find('form')
input.simulate('change', { target: { value: 'Yay' } })
expect(output.text()).toBe('Yay')
input.simulate('change', { target: { value: 'Yay!!' } })
expect(output.text()).toBe('Yay!!')
form.simulate('submit')
expect(submittedFormValue).toBe('Yay!!')
})
test('withHandlers passes immutable handlers', () => {
const enhance = withHandlers({
handler: () => () => null,
})
const component = sinon.spy(() => null)
const Div = enhance(component)
const wrapper = mount(<Div />)
wrapper.setProps({ foo: 'bar' })
expect(component.calledTwice).toBe(true)
expect(component.firstCall.args[0].handler).toBe(
component.secondCall.args[0].handler
)
})
test('withHandlers warns if handler is not a higher-order function', () => {
const error = sinon.stub(console, 'error')
const Button = withHandlers({
onClick: () => {},
})('button')
const wrapper = mount(<Button />)
const button = wrapper.find('button')
expect(() => button.simulate('click')).toThrowError(/undefined/)
expect(error.firstCall.args[0]).toBe(
'withHandlers(): Expected a map of higher-order functions. Refer to ' +
'the docs for more info.'
)
/* eslint-disable */
console.error.restore()
/* eslint-enable */
})
test('withHandlers allow handers to be a factory', () => {
const enhance = withHandlers(initialProps => {
let cache_
return {
handler: () => () => {
if (cache_) {
return cache_
}
cache_ = { ...initialProps }
return cache_
},
}
})
const componentHandlers = []
const componentHandlers2 = []
const Component = enhance(({ handler }) => {
componentHandlers.push(handler())
return null
})
const Component2 = enhance(({ handler }) => {
componentHandlers2.push(handler())
return null
})
const wrapper = mount(<Component hello={'foo'} />)
wrapper.setProps({ hello: 'bar' })
expect(componentHandlers[0]).toBe(componentHandlers[1])
// check that cache is not shared
mount(<Component2 hello={'foo'} />)
expect(componentHandlers[0]).toEqual(componentHandlers2[0])
expect(componentHandlers[0]).not.toBe(componentHandlers2[0])
})
================================================
FILE: src/packages/recompose/__tests__/withProps-test.js
================================================
import React from 'react'
import { shallow } from 'enzyme'
import { withProps } from '../'
test('withProps passes additional props to base component', () => {
const DoReMi = withProps({ 'data-so': 'do', 'data-la': 'fa' })('div')
expect(DoReMi.displayName).toBe('withProps(div)')
const div = shallow(<DoReMi />).find('div')
expect(div.prop('data-so')).toBe('do')
expect(div.prop('data-la')).toBe('fa')
})
test('withProps takes precedent over owner props', () => {
const DoReMi = withProps({ 'data-so': 'do', 'data-la': 'fa' })('div')
const div = shallow(<DoReMi data-la="ti" />).find('div')
expect(div.prop('data-so')).toBe('do')
expect(div.prop('data-la')).toBe('fa')
})
test('withProps should accept function', () => {
const DoReMi = withProps(props => ({
'data-so': props['data-la'],
}))('div')
const div = shallow(<DoReMi data-la="la" />).find('div')
expect(div.prop('data-so')).toBe('la')
})
================================================
FILE: src/packages/recompose/__tests__/withPropsOnChange-test.js
================================================
import * as React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import {
withPropsOnChange,
withState,
withStateHandlers,
flattenProp,
compose,
} from '../'
test('withPropsOnChange maps subset of owner props to child props', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const mapSpy = sinon.spy()
const StringConcat = compose(
withState('strings', 'updateStrings', { a: 'a', b: 'b', c: 'c' }),
flattenProp('strings'),
withPropsOnChange(['a', 'b'], ({ a, b, ...props }) => {
mapSpy()
return {
...props,
foobar: a + b,
}
})
)(component)
expect(StringConcat.displayName).toBe(
'withState(flattenProp(withPropsOnChange(component)))'
)
mount(<StringConcat />)
const { updateStrings } = component.firstCall.args[0]
expect(component.lastCall.args[0].foobar).toBe('ab')
expect(component.calledOnce).toBe(true)
expect(mapSpy.callCount).toBe(1)
// Does not re-map for non-dependent prop updates
updateStrings(strings => ({ ...strings, c: 'baz' }))
expect(component.lastCall.args[0].foobar).toBe('ab')
expect(component.lastCall.args[0].c).toBe('c')
expect(component.calledTwice).toBe(true)
expect(mapSpy.callCount).toBe(1)
updateStrings(strings => ({ ...strings, a: 'foo', b: 'bar' }))
expect(component.lastCall.args[0].foobar).toBe('foobar')
expect(component.lastCall.args[0].c).toBe('baz')
expect(component.calledThrice).toBe(true)
expect(mapSpy.callCount).toBe(2)
})
test('withPropsOnChange maps subset of owner props to child props with custom predicate', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const mapSpy = sinon.spy()
const shouldMapSpy = sinon.spy()
const PageContainer = compose(
withStateHandlers(
{ result: { hasError: false, loading: true, error: null } },
{
updateResult: ({ result }) => payload => ({
result: { ...result, ...payload },
}),
}
),
withPropsOnChange(
({ result }, { result: nextResult }) => {
shouldMapSpy(result, nextResult)
return !result.hasError && nextResult.hasError
},
({ result: { hasError, error } }) => {
mapSpy()
if (hasError) {
return {
errorEverHappened: true,
lastError: error,
}
}
return {
errorEverHappened: false,
}
}
)
)(component)
expect(PageContainer.displayName).toBe(
'withStateHandlers(withPropsOnChange(component))'
)
mount(<PageContainer />)
const { updateResult } = component.firstCall.args[0]
expect(component.lastCall.args[0].errorEverHappened).toBe(false)
expect(component.lastCall.args[0].lastError).toBeUndefined()
expect(component.calledOnce).toBe(true)
expect(mapSpy.callCount).toBe(1)
expect(shouldMapSpy.callCount).toBe(1)
updateResult({ loading: false, hasError: true, error: '1' })
expect(component.lastCall.args[0].errorEverHappened).toBe(true)
expect(component.lastCall.args[0].lastError).toBe('1')
expect(component.calledTwice).toBe(true)
expect(mapSpy.callCount).toBe(2)
// Does not re-map for false map result
updateResult({ loading: true, hasError: false, error: null })
expect(component.lastCall.args[0].errorEverHappened).toBe(true)
expect(component.lastCall.args[0].lastError).toBe('1')
expect(component.calledThrice).toBe(true)
expect(mapSpy.callCount).toBe(2)
updateResult({ loading: false, hasError: true, error: '2' })
expect(component.lastCall.args[0].errorEverHappened).toBe(true)
expect(component.lastCall.args[0].lastError).toBe('2')
expect(component.callCount).toBe(4)
expect(mapSpy.callCount).toBe(3)
})
================================================
FILE: src/packages/recompose/__tests__/withReducer-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { withReducer, compose, flattenProp } from '../'
const SET_COUNTER = 'SET_COUNTER'
test('adds a stateful value and a function for updating it', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const initialState = { counter: 0 }
const reducer = (state, action) =>
action.type === SET_COUNTER ? { counter: action.payload } : state
const Counter = compose(
withReducer('state', 'dispatch', reducer, initialState),
flattenProp('state')
)(component)
expect(Counter.displayName).toBe('withReducer(flattenProp(component))')
mount(<Counter />)
const { dispatch } = component.firstCall.args[0]
expect(component.lastCall.args[0].counter).toBe(0)
dispatch({ type: SET_COUNTER, payload: 18 })
expect(component.lastCall.args[0].counter).toBe(18)
})
test('calls initialState when it is a function', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const initialState = ({ initialCount }) => ({ counter: initialCount })
const reducer = (state, action) =>
action.type === SET_COUNTER ? { counter: action.payload } : state
const Counter = compose(
withReducer('state', 'dispatch', reducer, initialState),
flattenProp('state')
)(component)
mount(<Counter initialCount={10} />)
expect(component.lastCall.args[0].counter).toBe(10)
})
test('receives state from reducer when initialState is not provided', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const initialState = { counter: 0 }
const reducer = (state = initialState, action) =>
action.type === SET_COUNTER ? { counter: action.payload } : state
const Counter = compose(
withReducer('state', 'dispatch', reducer),
flattenProp('state')
)(component)
mount(<Counter />)
expect(component.lastCall.args[0].counter).toBe(0)
})
test('calls the given callback with new state after a dispatch call', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const initialState = { counter: 0 }
const reducer = (state, action) =>
action.type === SET_COUNTER ? { counter: action.payload } : state
const Counter = compose(
withReducer('state', 'dispatch', reducer, initialState),
flattenProp('state')
)(component)
mount(<Counter />)
const dispatch = sinon.spy(component.firstCall.args[0].dispatch)
const callback = sinon.spy()
dispatch({ type: SET_COUNTER, payload: 11 }, callback)
expect(dispatch.calledBefore(callback)).toBe(true)
expect(dispatch.calledOnce).toBe(true)
expect(callback.calledAfter(dispatch)).toBe(true)
expect(callback.calledOnce).toBe(true)
expect(callback.getCall(0).args.length).toBe(1)
expect(callback.getCall(0).args[0]).toEqual({ counter: 11 })
})
================================================
FILE: src/packages/recompose/__tests__/withState-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { withState } from '../'
test('withState adds a stateful value and a function for updating it', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withState('counter', 'updateCounter', 0)(component)
expect(Counter.displayName).toBe('withState(component)')
mount(<Counter pass="through" />)
const { updateCounter } = component.firstCall.args[0]
expect(component.lastCall.args[0].counter).toBe(0)
expect(component.lastCall.args[0].pass).toBe('through')
updateCounter(n => n + 9)
updateCounter(n => n * 2)
expect(component.lastCall.args[0].counter).toBe(18)
expect(component.lastCall.args[0].pass).toBe('through')
})
test('withState also accepts a non-function, which is passed directly to setState()', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withState('counter', 'updateCounter', 0)(component)
mount(<Counter />)
const { updateCounter } = component.firstCall.args[0]
updateCounter(18)
expect(component.lastCall.args[0].counter).toBe(18)
})
test('withState accepts setState() callback', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withState('counter', 'updateCounter', 0)(component)
mount(<Counter />)
const { updateCounter } = component.firstCall.args[0]
const renderSpy = sinon.spy(() => {
expect(component.lastCall.args[0].counter).toBe(18)
})
expect(component.lastCall.args[0].counter).toBe(0)
updateCounter(18, renderSpy)
})
test('withState also accepts initialState as function of props', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withState(
'counter',
'updateCounter',
props => props.initialCounter
)(component)
mount(<Counter initialCounter={1} />)
const { updateCounter } = component.firstCall.args[0]
expect(component.lastCall.args[0].counter).toBe(1)
updateCounter(n => n * 3)
expect(component.lastCall.args[0].counter).toBe(3)
})
================================================
FILE: src/packages/recompose/__tests__/withStateHandlers-test.js
================================================
import React from 'react'
import { mount } from 'enzyme'
import sinon from 'sinon'
import { compose, withStateHandlers } from '../'
test('withStateHandlers should persist events passed as argument', () => {
const component = ({ value, onChange }) =>
<div>
<input type="text" value={value} onChange={onChange} />
<p>
{value}
</p>
</div>
const InputComponent = withStateHandlers(
{ value: '' },
{
onChange: () => e => ({
value: e.target.value,
}),
}
)(component)
const wrapper = mount(<InputComponent />)
const input = wrapper.find('input')
const output = wrapper.find('p')
// having that enzyme simulate does not simulate real situation
// emulate persist
input.simulate('change', {
persist() {
this.target = { value: 'Yay' }
},
})
expect(output.text()).toBe('Yay')
input.simulate('change', { target: { value: 'empty' } })
expect(output.text()).toBe('empty')
})
test('withStateHandlers adds a stateful value and a function for updating it', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withStateHandlers(
{ counter: 0 },
{
updateCounter: ({ counter }) => increment => ({
counter: counter + increment,
}),
}
)(component)
expect(Counter.displayName).toBe('withStateHandlers(component)')
mount(<Counter pass="through" />)
const { updateCounter } = component.firstCall.args[0]
expect(component.lastCall.args[0].counter).toBe(0)
expect(component.lastCall.args[0].pass).toBe('through')
updateCounter(9)
expect(component.lastCall.args[0].counter).toBe(9)
updateCounter(1)
updateCounter(10)
expect(component.lastCall.args[0].counter).toBe(20)
expect(component.lastCall.args[0].pass).toBe('through')
})
test('withStateHandlers accepts initialState as function of props', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withStateHandlers(
({ initialCounter }) => ({
counter: initialCounter,
}),
{
updateCounter: ({ counter }) => increment => ({
counter: counter + increment,
}),
}
)(component)
const initialCounter = 101
mount(<Counter initialCounter={initialCounter} />)
expect(component.lastCall.args[0].counter).toBe(initialCounter)
})
test('withStateHandlers initial state must be function or object or null or undefined', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withStateHandlers(1, {})(component)
// React throws an error
// expect(() => mount(<Counter />)).toThrow()
const error = sinon.stub(console, 'error')
mount(<Counter />)
expect(error.called).toBe(true)
})
test('withStateHandlers have access to props', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withStateHandlers(
({ initialCounter }) => ({
counter: initialCounter,
}),
{
increment: ({ counter }, { incrementValue }) => () => ({
counter: counter + incrementValue,
}),
}
)(component)
const initialCounter = 101
const incrementValue = 37
mount(
<Counter initialCounter={initialCounter} incrementValue={incrementValue} />
)
const { increment } = component.firstCall.args[0]
increment()
expect(component.lastCall.args[0].counter).toBe(
initialCounter + incrementValue
)
})
test('withStateHandlers passes immutable state updaters', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withStateHandlers(
({ initialCounter }) => ({
counter: initialCounter,
}),
{
increment: ({ counter }, { incrementValue }) => () => ({
counter: counter + incrementValue,
}),
}
)(component)
const initialCounter = 101
const incrementValue = 37
mount(
<Counter initialCounter={initialCounter} incrementValue={incrementValue} />
)
const { increment } = component.firstCall.args[0]
increment()
expect(component.lastCall.args[0].counter).toBe(
initialCounter + incrementValue
)
})
test('withStateHandlers does not rerender if state updater returns undefined', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = withStateHandlers(
({ initialCounter }) => ({
counter: initialCounter,
}),
{
updateCounter: ({ counter }) => increment =>
increment === 0
? undefined
: {
counter: counter + increment,
},
}
)(component)
const initialCounter = 101
mount(<Counter initialCounter={initialCounter} />)
expect(component.callCount).toBe(1)
const { updateCounter } = component.firstCall.args[0]
updateCounter(1)
expect(component.callCount).toBe(2)
updateCounter(0)
expect(component.callCount).toBe(2)
})
test('withStateHandlers rerenders if parent props changed', () => {
const component = sinon.spy(() => null)
component.displayName = 'component'
const Counter = compose(
withStateHandlers(
({ initialCounter }) => ({
counter: initialCounter,
}),
{
increment: ({ counter }) => incrementValue => ({
counter: counter + incrementValue,
}),
}
),
withStateHandlers(
{ incrementValue: 1 },
{
// updates parent state and return undefined
updateParentIncrement: ({ incrementValue }, { increment }) => () => {
increment(incrementValue)
return undefined
},
}
)
)(component)
const initialCounter = 101
mount(<Counter initialCounter={initialCounter} />)
const { updateParentIncrement } = component.firstCall.args[0]
updateParentIncrement()
expect(component.lastCall.args[0].counter).toBe(initialCounter + 1)
})
================================================
FILE: src/packages/recompose/__tests__/wrapDisplayName-test.js
================================================
import React from 'react'
import { wrapDisplayName } from '../'
test('wrapDisplayName wraps the display name of a React component with the name of an HoC, Relay-style', () => {
class SomeComponent extends React.Component {
render() {
return <div />
}
}
expect(wrapDisplayName(SomeComponent, 'someHoC')).toBe(
'someHoC(SomeComponent)'
)
})
================================================
FILE: src/packages/recompose/baconObservableConfig.js
================================================
import $$observable from 'symbol-observable'
import Bacon from 'baconjs'
const config = {
fromESObservable: observable =>
Bacon.fromBinder(sink => {
const { unsubscribe } = observable.subscribe({
next: val => sink(new Bacon.Next(val)),
error: err => sink(new Bacon.Error(err)),
complete: () => sink(new Bacon.End()),
})
return unsubscribe
}),
toESObservable: stream => ({
subscribe: observer => {
const unsubscribe = stream.subscribe(event => {
if (event.hasValue()) {
observer.next(event.value())
} else if (event.isError()) {
observer.error(event.error)
} else if (event.isEnd()) {
observer.complete()
}
})
return { unsubscribe }
},
[$$observable]() {
return this
},
}),
}
export default config
================================================
FILE: src/packages/recompose/branch.js
================================================
import { createFactory } from 'react'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const identity = Component => Component
const branch = (test, left, right = identity) => BaseComponent => {
let leftFactory
let rightFactory
const Branch = props => {
if (test(props)) {
leftFactory = leftFactory || createFactory(left(BaseComponent))
return leftFactory(props)
}
rightFactory = rightFactory || createFactory(right(BaseComponent))
return rightFactory(props)
}
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(BaseComponent, 'branch'))(Branch)
}
return Branch
}
export default branch
================================================
FILE: src/packages/recompose/componentFromProp.js
================================================
import { createElement } from 'react'
import omit from './utils/omit'
const componentFromProp = propName => {
const Component = props =>
createElement(props[propName], omit(props, [propName]))
Component.displayName = `componentFromProp(${propName})`
return Component
}
export default componentFromProp
================================================
FILE: src/packages/recompose/componentFromStream.js
================================================
import { Component } from 'react'
import { createChangeEmitter } from 'change-emitter'
import $$observable from 'symbol-observable'
import { config as globalConfig } from './setObservableConfig'
export const componentFromStreamWithConfig = config => propsToVdom =>
class ComponentFromStream extends Component {
state = { vdom: null }
propsEmitter = createChangeEmitter()
// Stream of props
props$ = config.fromESObservable({
subscribe: observer => {
const unsubscribe = this.propsEmitter.listen(props => {
if (props) {
observer.next(props)
} else {
observer.complete()
}
})
return { unsubscribe }
},
[$$observable]() {
return this
},
})
// Stream of vdom
vdom$ = config.toESObservable(propsToVdom(this.props$))
componentWillMount() {
// Subscribe to child prop changes so we know when to re-render
this.subscription = this.vdom$.subscribe({
next: vdom => {
this.setState({ vdom })
},
})
this.propsEmitter.emit(this.props)
}
componentWillReceiveProps(nextProps) {
// Receive new props from the owner
this.propsEmitter.emit(nextProps)
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.vdom !== this.state.vdom
}
componentWillUnmount() {
// Call without arguments to complete stream
this.propsEmitter.emit()
// Clean-up subscription before un-mounting
this.subscription.unsubscribe()
}
render() {
return this.state.vdom
}
}
const componentFromStream = propsToVdom =>
componentFromStreamWithConfig(globalConfig)(propsToVdom)
export default componentFromStream
================================================
FILE: src/packages/recompose/compose.js
================================================
const compose = (...funcs) =>
funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg)
export default compose
================================================
FILE: src/packages/recompose/createEventHandler.js
================================================
import $$observable from 'symbol-observable'
import { createChangeEmitter } from 'change-emitter'
import { config as globalConfig } from './setObservableConfig'
export const createEventHandlerWithConfig = config => () => {
const emitter = createChangeEmitter()
const stream = config.fromESObservable({
subscribe(observer) {
const unsubscribe = emitter.listen(value => observer.next(value))
return { unsubscribe }
},
[$$observable]() {
return this
},
})
return {
handler: emitter.emit,
stream,
}
}
const createEventHandler = createEventHandlerWithConfig(globalConfig)
export default createEventHandler
================================================
FILE: src/packages/recompose/createSink.js
================================================
import { Component } from 'react'
import { polyfill } from 'react-lifecycles-compat'
const createSink = callback => {
class Sink extends Component {
state = {}
static getDerivedStateFromProps(nextProps) {
callback(nextProps)
return null
}
render() {
return null
}
}
polyfill(Sink)
return Sink
}
export default createSink
================================================
FILE: src/packages/recompose/defaultProps.js
================================================
import { createFactory } from 'react'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const defaultProps = props => BaseComponent => {
const factory = createFactory(BaseComponent)
const DefaultProps = ownerProps => factory(ownerProps)
DefaultProps.defaultProps = props
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(BaseComponent, 'defaultProps'))(
DefaultProps
)
}
return DefaultProps
}
export default defaultProps
================================================
FILE: src/packages/recompose/flattenProp.js
================================================
import { createFactory } from 'react'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const flattenProp = propName => BaseComponent => {
const factory = createFactory(BaseComponent)
const FlattenProp = props =>
factory({
...props,
...props[propName],
})
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(BaseComponent, 'flattenProp'))(
FlattenProp
)
}
return FlattenProp
}
export default flattenProp
================================================
FILE: src/packages/recompose/flydObservableConfig.js
================================================
import $$observable from 'symbol-observable'
import flyd from 'flyd'
const noop = () => {}
const config = {
fromESObservable: observable => {
const stream = flyd.stream()
const { unsubscribe } = observable.subscribe({
next: value => stream(value),
error: error => stream({ error }),
complete: () => stream.end(true),
})
flyd.on(unsubscribe, stream.end)
return stream
},
toESObservable: stream => ({
subscribe: observer => {
const sub = flyd.on(observer.next || noop, stream)
flyd.on(_ => observer.complete(), sub.end)
return {
unsubscribe: () => sub.end(true),
}
},
[$$observable]() {
return this
},
}),
}
export default config
================================================
FILE: src/packages/recompose/fromRenderProps.js
================================================
import React from 'react'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const fromRenderProps = (
RenderPropsComponent,
propsMapper,
renderPropName = 'children'
) => BaseComponent => {
const baseFactory = React.createFactory(BaseComponent)
const renderPropsFactory = React.createFactory(RenderPropsComponent)
const FromRenderProps = ownerProps =>
renderPropsFactory({
[renderPropName]: (...props) =>
baseFactory({ ...ownerProps, ...propsMapper(...props) }),
})
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(BaseComponent, 'fromRenderProps'))(
FromRenderProps
)
}
return FromRenderProps
}
export default fromRenderProps
================================================
FILE: src/packages/recompose/getContext.js
================================================
import { createFactory } from 'react'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const getContext = contextTypes => BaseComponent => {
const factory = createFactory(BaseComponent)
const GetContext = (ownerProps, context) =>
factory({
...ownerProps,
...context,
})
GetContext.contextTypes = contextTypes
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(BaseComponent, 'getContext'))(
GetContext
)
}
return GetContext
}
export default getContext
================================================
FILE: src/packages/recompose/getDisplayName.js
================================================
const getDisplayName = Component => {
if (typeof Component === 'string') {
return Component
}
if (!Component) {
return undefined
}
return Component.displayName || Component.name || 'Component'
}
export default getDisplayName
================================================
FILE: src/packages/recompose/hoistStatics.js
================================================
import hoistNonReactStatics from 'hoist-non-react-statics'
const hoistStatics = (higherOrderComponent, blacklist) => BaseComponent => {
const NewComponent = higherOrderComponent(BaseComponent)
hoistNonReactStatics(NewComponent, BaseComponent, blacklist)
return NewComponent
}
export default hoistStatics
================================================
FILE: src/packages/recompose/index.js
================================================
// Higher-order component helpers
export { default as mapProps } from './mapProps'
export { default as withProps } from './withProps'
export { default as withPropsOnChange } from './withPropsOnChange'
export { default as withHandlers } from './withHandlers'
export { default as defaultProps } from './defaultProps'
export { default as renameProp } from './renameProp'
export { default as renameProps } from './renameProps'
export { default as flattenProp } from './flattenProp'
export { default as withState } from './withState'
export { default as withStateHandlers } from './withStateHandlers'
export { default as withReducer } from './withReducer'
export { default as branch } from './branch'
export { default as renderComponent } from './renderComponent'
export { default as renderNothing } from './renderNothing'
export { default as shouldUpdate } from './shouldUpdate'
export { default as pure } from './pure'
export { default as onlyUpdateForKeys } from './onlyUpdateForKeys'
export { default as onlyUpdateForPropTypes } from './onlyUpdateForPropTypes'
export { default as withContext } from './withContext'
export { default as getContext } from './getContext'
export { default as lifecycle } from './lifecycle'
export { default as toClass } from './toClass'
export { default as toRenderProps } from './toRenderProps'
export { default as fromRenderProps } from './fromRenderProps'
// Static property helpers
export { default as setStatic } from './setStatic'
export { default as setPropTypes } from './setPropTypes'
export { default as setDisplayName } from './setDisplayName'
// Composition function
export { default as compose } from './compose'
// Other utils
export { default as getDisplayName } from './getDisplayName'
export { default as wrapDisplayName } from './wrapDisplayName'
export { default as shallowEqual } from './shallowEqual'
export { default as isClassComponent } from './isClassComponent'
export { default as createSink } from './createSink'
export { default as componentFromProp } from './componentFromProp'
export { default as nest } from './nest'
export { default as hoistStatics } from './hoistStatics'
// Observable helpers
export {
default as componentFromStream,
componentFromStreamWithConfig,
} from './componentFromStream'
export {
default as mapPropsStream,
mapPropsStreamWithConfig,
} from './mapPropsStream'
export {
default as createEventHandler,
createEventHandlerWithConfig,
} from './createEventHandler'
export { default as setObservableConfig } from './setObservableConfig'
================================================
FILE: src/packages/recompose/index.js.flow
================================================
/* eslint-disable */
/* @flow strict */
/**
* 1) Types give additional constraint on a language, recompose was written on the untyped language
* as a consequence of this fact
* for some recompose HOCs is near impossible to add correct typings.
* 2) flow sometimes does not work as expected.
*
* So any help and suggestions will be very appreciated.
*
* -----------------------------------------------------------------------------------
* Type definition of recompose HOCs are splitted into 2 parts,
* "HOCs with good flow support" - in most cases you can use them without big issues,
* see `test_${hocName}.js` for the idea.
* Some known issues:
* see test_mapProps.js - inference work but type errors are not detected in hocs
*
* SUPPORTED HOCs:
* defaultProps, mapProps, withProps, withStateHandlers, withHandlers, pure,
* onlyUpdateForKeys, shouldUpdate, renderNothing, renderComponent, branch, withPropsOnChange,
* onlyUpdateForPropTypes, toClass, withContext, getContext,
* setStatic, setPropTypes, setDisplayName,
* -----------------------------------------------------------------------------------
* "TODO (UNSUPPORTED) HOCs" - you need to provide type information
* (no automatic type inference), voodoo dancing etc
* see `test_voodoo.js` for the idea
*
* remember that:
* 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
* -----------------------------------------------------------------------------------
*
* utils:
* getDisplayName, wrapDisplayName, shallowEqual,
* isClassComponent, createSink, componentFromProp,
* nest, hoistStatics,
*/
//-------------------
// -----------------------------------------------------------------
// Private declarations
// -----------------------------------------------------------------
declare type ExtractToVoid = <A, B, C, R>(
v: (a: A, b: B, c: C) => R
) => (A, B, C) => void
declare type ExtractStateHandlersCodomain = <State, Enhanced, V>(
v: (state: State, props: Enhanced) => V
) => V
declare type ExtractHandlersCodomain = <Enhanced, V>(
v: (props: Enhanced) => V
) => V
declare type UnaryFn<A, R> = (a: A) => R
// -----------------------------------------------------------------
// Public declarations
// -----------------------------------------------------------------
export type Component<A> = React$ComponentType<A>
export type HOC<Base, Enhanced> = UnaryFn<Component<Base>, Component<Enhanced>>
declare export var compose: $Compose
// ---------------------------------------------------------------------------
// ----------------===<<<HOCs with good flow support>>>===--------------------
// ---------------------------------------------------------------------------
declare export function defaultProps<Default, Enhanced>(
defProps: Default
): HOC<{ ...$Exact<Enhanced>, ...Default }, Enhanced>
declare export function mapProps<Base, Enhanced>(
propsMapper: (ownerProps: Enhanced) => Base
): HOC<Base, Enhanced>
declare export function withProps<BaseAdd, Enhanced>(
propsMapper: ((ownerProps: Enhanced) => BaseAdd) | BaseAdd
): HOC<{ ...$Exact<Enhanced>, ...BaseAdd }, Enhanced>
declare export function withStateHandlers<
State,
Enhanced,
StateHandlers: {
[key: string]: (
state: State,
props: Enhanced
) => (...payload: any[]) => $Shape<State>,
}
>(
initialState: ((props: Enhanced) => State) | State,
stateUpdaters: StateHandlers
): HOC<
{
...$Exact<Enhanced>,
...$Exact<State>,
...$ObjMap<
$ObjMap<StateHandlers, ExtractStateHandlersCodomain>,
ExtractToVoid
>,
},
Enhanced
>
declare export function withHandlers<
Enhanced,
Handlers:
| ((
props: Enhanced
) => {
[key: string]: (props: Enhanced) => Function,
})
| {
[key: string]: (props: Enhanced) => Function,
}
>(
handlers: ((props: Enhanced) => Handlers) | Handlers
): HOC<
{
...$Exact<Enhanced>,
...$ObjMap<Handlers, ExtractHandlersCodomain>,
},
Enhanced
>
declare export function pure<A>(a: Component<A>): Component<A>
declare export function onlyUpdateForPropTypes<A>(a: Component<A>): Component<A>
declare export function onlyUpdateForKeys<A>(Array<$Keys<A>>): HOC<A, A>
declare export function shouldUpdate<A>(
(props: A, nextProps: A) => boolean
): HOC<A, A>
declare export function toClass<A>(a: Component<A>): Component<A>
declare export function withContext<A, ContextPropTypes, ContextObj>(
childContextTypes: ContextPropTypes,
getChildContext: (props: A) => ContextObj
): HOC<A, A>
declare export function getContext<CtxTypes, Enhanced>(
contextTypes: CtxTypes
): HOC<{ ...$Exact<Enhanced>, ...CtxTypes }, Enhanced>
/**
* It's wrong declaration but having that renderNothing and renderComponent are somehow useless
* outside branch enhancer, we just give it an id type
* so common way of using branch like
* `branch(testFn, renderNothing | renderComponent(Comp))` will work as expected.
* Tests are placed at test_branch.
*/
declare export function renderNothing<A>(C: Component<A>): Component<A>
declare export function renderComponent<A>(a: Component<A>): HOC<A, A>
/**
* We make an assumtion that left and right have the same type if exists
*/
declare export function branch<Base, Enhanced>(
testFn: (props: Enhanced) => boolean,
// not a HOC because of inference problems, this works but HOC<Base, Enhanced> is not
left: (Component<Base>) => Component<Enhanced>,
// I never use right part and it can be a problem with inference as should be same type as left
right?: (Component<Base>) => Component<Enhanced>
): HOC<Base, Enhanced>
// test_statics
declare export function setStatic<A>(key: string, value: any): HOC<A, A>
declare export function setPropTypes<A>(propTypes: Object): HOC<A, A>
declare export function setDisplayName<A>(displayName: string): HOC<A, A>
declare export function withPropsOnChange<BaseAdd, Enhanced>(
shouldMapOrKeys:
| ((props: Enhanced, nextProps: Enhanced) => boolean)
| Array<$Keys<Enhanced>>,
propsMapper: (ownerProps: Enhanced) => BaseAdd
): HOC<{ ...$Exact<Enhanced>, ...BaseAdd }, Enhanced>
// ---------------------------------------------------------------------------
// ----------------===<<<TODO (UNSUPPORTED) HOCs>>>===------------------------
// ---------------------------------------------------------------------------
// use withProps instead
declare export function flattenProp<Base, Enhanced>(
propName: $Keys<Enhanced>
): HOC<Base, Enhanced>
// use withProps instead
declare export function renameProp<Base, Enhanced>(
oldName: $Keys<Enhanced>,
newName: $Keys<Base>
): HOC<Base, Enhanced>
// use withProps instead
declare export function renameProps<Base, Enhanced>(nameMap: {
[key: $Keys<Enhanced>]: $Keys<Base>,
}): HOC<Base, Enhanced>
// use withStateHandlers instead
declare export function withState<Base, Enhanced, T>(
stateName: string,
stateUpdaterName: string,
initialState: T | ((props: Enhanced) => T)
): HOC<Base, Enhanced>
// use withStateHandlers instead
declare export function withReducer<A, B, Action, State>(
stateName: string,
dispatchName: string,
reducer: (state: State, action: Action) => State,
initialState: State
): HOC<A, B>
// lifecycle use React instead
declare export function lifecycle<A, B>(spec: Object): HOC<A, B>
// Help needed, as explicitly providing the type
// errors not detected, see TODO at test_mapPropsStream.js
declare export function mapPropsStream<Base, Enhanced>(
(props$: any) => any
): HOC<Base, Enhanced>
// ---------------------------------------------------------------------------
// -----------------------------===<<<Utils>>>===-----------------------------
// ---------------------------------------------------------------------------
declare export function getDisplayName<A>(C: Component<A>): string
declare export function wrapDisplayName<A>(
C: Component<A>,
wrapperName: string
): string
declare export function shallowEqual(objA: mixed, objB: mixed): boolean
declare export function isClassComponent(value: any): boolean
declare export function createSink<A>(
callback: (props: A) => void
): Component<A>
declare export function componentFromProp<A>(propName: string): Component<A>
declare export function nest<A>(
...Components: Array<Component<any> | string>
): Component<A>
declare export function hoistStatics<A, B, H: HOC<A, B>>(hoc: H): H
declare export function componentFromStream<T>(
(props$: any) => any
): T => React$Element<any>
declare export function createEventHandler(): {
stream: any,
handler: Function,
}
declare export function toRenderProps<B, E>(
hoc: HOC<B, E>
): React$ComponentType<{|
...$Exact<E>,
children: (b: B) => React$Node,
|}>
declare export function fromRenderProps<B, E, RenderPropName, Props>(
RenderPropsComponent: Component<{
[RenderPropName]: (...props: Props) => React$Node,
}>,
propsMapper: ((...props: Props) => B),
renderPropName?: RenderPropName
): HOC<{ ...$Exact<E>, ...B }, E>
================================================
FILE: src/packages/recompose/isClassComponent.js
================================================
const isClassComponent = Component =>
Boolean(
Component &&
Component.prototype &&
typeof Component.prototype.render === 'function'
)
export default isClassComponent
================================================
FILE: src/packages/recompose/kefirObservableConfig.js
================================================
import Kefir from 'kefir'
const config = {
fromESObservable: Kefir.fromESObservable,
toESObservable: stream => stream.toESObservable(),
}
export default config
================================================
FILE: src/packages/recompose/lifecycle.js
================================================
/* eslint-disable no-console */
import { createFactory, Component } from 'react'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const lifecycle = spec => BaseComponent => {
const factory = createFactory(BaseComponent)
if (process.env.NODE_ENV !== 'production' && spec.hasOwnProperty('render')) {
console.error(
'lifecycle() does not support the render method; its behavior is to ' +
'pass all props and state to the base component.'
)
}
class Lifecycle extends Component {
render() {
return factory({
...this.props,
...this.state,
})
}
}
Object.keys(spec).forEach(hook => (Lifecycle.prototype[hook] = spec[hook]))
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(BaseComponent, 'lifecycle'))(
Lifecycle
)
}
return Lifecycle
}
export default lifecycle
================================================
FILE: src/packages/recompose/mapProps.js
================================================
import { createFactory } from 'react'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const mapProps = propsMapper => BaseComponent => {
const factory = createFactory(BaseComponent)
const MapProps = props => factory(propsMapper(props))
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(BaseComponent, 'mapProps'))(MapProps)
}
return MapProps
}
export default mapProps
================================================
FILE: src/packages/recompose/mapPropsStream.js
================================================
import { createFactory } from 'react'
import $$observable from 'symbol-observable'
import { componentFromStreamWithConfig } from './componentFromStream'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
import { config as globalConfig } from './setObservableConfig'
const identity = t => t
export const mapPropsStreamWithConfig = config => {
const componentFromStream = componentFromStreamWithConfig({
fromESObservable: identity,
toESObservable: identity,
})
return transform => BaseComponent => {
const factory = createFactory(BaseComponent)
const { fromESObservable, toESObservable } = config
return componentFromStream(props$ => ({
subscribe(observer) {
const subscription = toESObservable(
transform(fromESObservable(props$))
).subscribe({
next: childProps => observer.next(factory(childProps)),
})
return {
unsubscribe: () => subscription.unsubscribe(),
}
},
[$$observable]() {
return this
},
}))
}
}
const mapPropsStream = transform => {
const hoc = mapPropsStreamWithConfig(globalConfig)(transform)
if (process.env.NODE_ENV !== 'production') {
return BaseComponent =>
setDisplayName(wrapDisplayName(BaseComponent, 'mapPropsStream'))(
hoc(BaseComponent)
)
}
return hoc
}
export default mapPropsStream
================================================
FILE: src/packages/recompose/mostObservableConfig.js
================================================
import { from, Stream } from 'most'
const config = {
fromESObservable: from || Stream.from,
toESObservable: stream => stream,
}
export default config
================================================
FILE: src/packages/recompose/nest.js
================================================
import { createFactory } from 'react'
import getDisplayName from './getDisplayName'
const nest = (...Components) => {
const factories = Components.map(createFactory)
const Nest = ({ children, ...props }) =>
factories.reduceRight((child, factory) => factory(props, child), children)
if (process.env.NODE_ENV !== 'production') {
const displayNames = Components.map(getDisplayName)
Nest.displayName = `nest(${displayNames.join(', ')})`
}
return Nest
}
export default nest
================================================
FILE: src/packages/recompose/onlyUpdateForKeys.js
================================================
import shouldUpdate from './shouldUpdate'
import shallowEqual from './shallowEqual'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
import pick from './utils/pick'
const onlyUpdateForKeys = propKeys => {
const hoc = shouldUpdate(
(props, nextProps) =>
!shallowEqual(pick(nextProps, propKeys), pick(props, propKeys))
)
if (process.env.NODE_ENV !== 'production') {
return BaseComponent =>
setDisplayName(wrapDisplayName(BaseComponent, 'onlyUpdateForKeys'))(
hoc(BaseComponent)
)
}
return hoc
}
export default onlyUpdateForKeys
================================================
FILE: src/packages/recompose/onlyUpdateForPropTypes.js
================================================
import onlyUpdateForKeys from './onlyUpdateForKeys'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
import getDisplayName from './getDisplayName'
const onlyUpdateForPropTypes = BaseComponent => {
const propTypes = BaseComponent.propTypes
if (process.env.NODE_ENV !== 'production') {
if (!propTypes) {
/* eslint-disable */
console.error(
'A component without any `propTypes` was passed to ' +
'`onlyUpdateForPropTypes()`. Check the implementation of the ' +
`component with display name "${getDisplayName(BaseComponent)}".`
)
/* eslint-enable */
}
}
const propKeys = Object.keys(propTypes || {})
const OnlyUpdateForPropTypes = onlyUpdateForKeys(propKeys)(BaseComponent)
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(
wrapDisplayName(BaseComponent, 'onlyUpdateForPropTypes')
)(OnlyUpdateForPropTypes)
}
return OnlyUpdateForPropTypes
}
export default onlyUpdateForPropTypes
================================================
FILE: src/packages/recompose/package.json
================================================
{
"description": "A React utility belt for function components and higher-order components",
"keywords": [
"react",
"higher-order",
"components",
"microcomponentization",
"toolkit",
"utilities",
"composition"
],
"main": "dist/Recompose.cjs.js",
"module": "dist/Recompose.esm.js",
"dependencies": {
"@babel/runtime": "^7.0.0",
"change-emitter": "^0.1.2",
"hoist-non-react-statics": "^2.3.1",
"react-lifecycles-compat": "^3.0.2",
"symbol-observable": "^1.0.4"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0 || ^16.0.0"
}
}
================================================
FILE: src/packages/recompose/pure.js
================================================
import shouldUpdate from './shouldUpdate'
import shallowEqual from './shallowEqual'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const pure = BaseComponent => {
const hoc = shouldUpdate(
(props, nextProps) => !shallowEqual(props, nextProps)
)
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(BaseComponent, 'pure'))(
hoc(BaseComponent)
)
}
return hoc(BaseComponent)
}
export default pure
================================================
FILE: src/packages/recompose/renameProp.js
================================================
import omit from './utils/omit'
import mapProps from './mapProps'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const renameProp = (oldName, newName) => {
const hoc = mapProps(props => ({
...omit(props, [oldName]),
[newName]: props[oldName],
}))
if (process.env.NODE_ENV !== 'production') {
return BaseComponent =>
setDisplayName(wrapDisplayName(BaseComponent, 'renameProp'))(
hoc(BaseComponent)
)
}
return hoc
}
export default renameProp
================================================
FILE: src/packages/recompose/renameProps.js
================================================
import omit from './utils/omit'
import pick from './utils/pick'
import mapProps from './mapProps'
import setDisplayName from './setDisplayName'
import wrapDisplayName from './wrapDisplayName'
const { keys } = Object
const mapKeys = (obj, func) =>
keys(obj).reduce((result, key) => {
const val = obj[key]
/* eslint-disable no-param-reassign */
result[func(val, key)] = val
/* eslint-enable no-param-reassign */
return result
}, {})
const renameProps = nameMap => {
const hoc = mapProps(props => ({
...omit(props, keys(nameMap)),
...mapKeys(pick(props, keys(nameMap)), (_, oldName) => nameMap[oldName]),
}))
if (process.env.NODE_ENV !== 'production') {
return BaseComponent =>
setDisplayName(wrapDisplayName(BaseComponent, 'renameProps'))(
hoc(BaseComponent)
)
}
return hoc
}
export default renameProps
================================================
FILE: src/packages/recompose/renderComponent.js
================================================
import { createFactory } from 'react'
import wrapDisplayName from './wrapDisplayName'
const renderComponent = Component => _ => {
const factory = createFactory(Component)
const RenderComponent = props => factory(props)
if (process.env.NODE_ENV !== 'production') {
RenderComponent.displayName = wrapDisplayName(Component, 'renderComponent')
}
return RenderComponent
}
export default renderComponent
================================================
FILE: src/packages/recompose/renderNothing.js
================================================
import { Component } from 'react'
class Nothing extends Component {
render() {
return null
}
}
const renderNothing = _ => Nothing
export default renderNothing
================================================
FILE: src/packages/recompose/rxjs4ObservableConfig.js
================================================
import $$observable from 'symbol-observable'
import Rx from 'rx'
const config = {
fromESObservable: observable =>
Rx.Observable.create(observer => {
const { unsubscribe } = observable.subscribe({
next: val => observer.onNext(val),
error: error => observer.onError(error),
complete: () => observer.onCompleted(),
})
return unsubscribe
}),
toESObservable: rxObservable => ({
subscribe: observer => {
const subscription = rxObservable.subscribe(
val => observer.next(val),
error => observer.error(error),
() => observer.complete()
)
return { unsubscribe: () => subscription.dispose() }
},
[$$observable]() {
return this
},
}),
}
export default config
================================================
FILE: src/packages/recompose/rxjsObservableConfig.js
================================================
import Rx from 'rxjs'
const config = {
fromESObservable: Rx.Observable.from,
toESObservable: stream => stream,
}
export default config
================================================
FILE: src/packages/recompose/setDisplayName.js
================================================
import setStatic from './setStatic'
const setDisplayName = displayName => setStatic('displayName', displayName)
export default setDisplayName
================================================
FILE: src/packages/recompose/setObservableConfig.js
================================================
let _config = {
fromESObservable: null,
toESObservable: null,
}
const configureObservable = c => {
_config = c
}
export const config = {
fromESObservable: observable =>
typeof _config.fromESObservable === 'function'
? _config.fromESObse
gitextract_7mr7594c/
├── .codeclimate.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .flowconfig
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc
├── .size-snapshot.json
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── babel.config.js
├── docs/
│ ├── API.md
│ ├── flow.md
│ └── performance.md
├── package.json
├── scripts/
│ ├── getPackageNames.js
│ ├── installNestedPackageDeps.js
│ ├── jest.setup.js
│ ├── release.js
│ └── rollup.config.js
├── src/
│ ├── basePackage.json
│ └── packages/
│ ├── recompose/
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── VERSION
│ │ ├── __tests__/
│ │ │ ├── branch-test.js
│ │ │ ├── componentFromProp-test.js
│ │ │ ├── componentFromStream-test.js
│ │ │ ├── componentFromStreamWithConfig-test.js
│ │ │ ├── compose-test.js
│ │ │ ├── createEventHandler-test.js
│ │ │ ├── createSink-test.js
│ │ │ ├── defaultProps-test.js
│ │ │ ├── fixtures/
│ │ │ │ └── treeshake-entry.js
│ │ │ ├── flattenProp-test.js
│ │ │ ├── fromRenderProps-test.js
│ │ │ ├── getContext-test.js
│ │ │ ├── getDisplayName-test.js
│ │ │ ├── hoistStatics-test.js
│ │ │ ├── isClassComponent-test.js
│ │ │ ├── lifecycle-test.js
│ │ │ ├── mapProps-test.js
│ │ │ ├── mapPropsStream-test.js
│ │ │ ├── mapPropsStreamWithConfig-test.js
│ │ │ ├── nest-test.js
│ │ │ ├── onlyUpdateForKeys-test.js
│ │ │ ├── onlyUpdateForPropTypes-test.js
│ │ │ ├── pure-test.js
│ │ │ ├── renameProp-test.js
│ │ │ ├── renameProps-test.js
│ │ │ ├── renderComponent-test.js
│ │ │ ├── renderNothing-test.js
│ │ │ ├── setDisplayName-test.js
│ │ │ ├── setObservableConfig-test.js
│ │ │ ├── setPropTypes-test.js
│ │ │ ├── setStatic-test.js
│ │ │ ├── shallowEqual-test.js
│ │ │ ├── shouldUpdate-test.js
│ │ │ ├── toClass-test.js
│ │ │ ├── toRenderProps-test.js
│ │ │ ├── types/
│ │ │ │ ├── test_branch.js
│ │ │ │ ├── test_classBasedEnhancer.js
│ │ │ │ ├── test_componentFromStream.js
│ │ │ │ ├── test_createEventHandler.js
│ │ │ │ ├── test_defaultProps.js
│ │ │ │ ├── test_fromRenderProps.js
│ │ │ │ ├── test_functionalEnhancer.js
│ │ │ │ ├── test_getContext.js
│ │ │ │ ├── test_mapProps.js
│ │ │ │ ├── test_mapPropsStream.js
│ │ │ │ ├── test_onlyUpdateForKeys.js
│ │ │ │ ├── test_onlyUpdateForPropTypes.js
│ │ │ │ ├── test_pure.js
│ │ │ │ ├── test_shouldUpdate.js
│ │ │ │ ├── test_statics.js
│ │ │ │ ├── test_toClass.js
│ │ │ │ ├── test_toRenderProps.js
│ │ │ │ ├── test_utils.js
│ │ │ │ ├── test_voodoo.js
│ │ │ │ ├── test_withContext.js
│ │ │ │ ├── test_withHandlers.js
│ │ │ │ ├── test_withProps.js
│ │ │ │ ├── test_withPropsOnChange.js
│ │ │ │ └── test_withStateHandlers.js
│ │ │ ├── utils.js
│ │ │ ├── withContext-test.js
│ │ │ ├── withHandlers-test.js
│ │ │ ├── withProps-test.js
│ │ │ ├── withPropsOnChange-test.js
│ │ │ ├── withReducer-test.js
│ │ │ ├── withState-test.js
│ │ │ ├── withStateHandlers-test.js
│ │ │ └── wrapDisplayName-test.js
│ │ ├── baconObservableConfig.js
│ │ ├── branch.js
│ │ ├── componentFromProp.js
│ │ ├── componentFromStream.js
│ │ ├── compose.js
│ │ ├── createEventHandler.js
│ │ ├── createSink.js
│ │ ├── defaultProps.js
│ │ ├── flattenProp.js
│ │ ├── flydObservableConfig.js
│ │ ├── fromRenderProps.js
│ │ ├── getContext.js
│ │ ├── getDisplayName.js
│ │ ├── hoistStatics.js
│ │ ├── index.js
│ │ ├── index.js.flow
│ │ ├── isClassComponent.js
│ │ ├── kefirObservableConfig.js
│ │ ├── lifecycle.js
│ │ ├── mapProps.js
│ │ ├── mapPropsStream.js
│ │ ├── mostObservableConfig.js
│ │ ├── nest.js
│ │ ├── onlyUpdateForKeys.js
│ │ ├── onlyUpdateForPropTypes.js
│ │ ├── package.json
│ │ ├── pure.js
│ │ ├── renameProp.js
│ │ ├── renameProps.js
│ │ ├── renderComponent.js
│ │ ├── renderNothing.js
│ │ ├── rxjs4ObservableConfig.js
│ │ ├── rxjsObservableConfig.js
│ │ ├── setDisplayName.js
│ │ ├── setObservableConfig.js
│ │ ├── setPropTypes.js
│ │ ├── setStatic.js
│ │ ├── shallowEqual.js
│ │ ├── shouldUpdate.js
│ │ ├── toClass.js
│ │ ├── toRenderProps.js
│ │ ├── utils/
│ │ │ ├── mapValues.js
│ │ │ ├── omit.js
│ │ │ └── pick.js
│ │ ├── withContext.js
│ │ ├── withHandlers.js
│ │ ├── withProps.js
│ │ ├── withPropsOnChange.js
│ │ ├── withReducer.js
│ │ ├── withState.js
│ │ ├── withStateHandlers.js
│ │ ├── wrapDisplayName.js
│ │ └── xstreamObservableConfig.js
│ └── recompose-relay/
│ ├── .npmignore
│ ├── README.md
│ ├── VERSION
│ ├── createContainer.js
│ ├── index.js
│ └── package.json
└── types/
├── README.md
└── flow-example/
├── .eslintrc
├── .flowconfig
├── .gitignore
├── README.md
├── flow-typed/
│ ├── glamor.js
│ └── react-motion.js
├── package.json
├── public/
│ ├── index.html
│ └── manifest.json
└── src/
├── App.js
├── Item.js
├── ItemsAnimator.js
├── MouseDetector.js
└── index.js
SYMBOL INDEX (69 symbols across 32 files)
FILE: scripts/release.js
constant BIN (line 12) | const BIN = './node_modules/.bin'
constant BASE_PACKAGE_LOC (line 20) | const BASE_PACKAGE_LOC = '../src/basePackage.json'
FILE: src/packages/recompose/__tests__/componentFromStream-test.js
method unsubscribe (line 31) | unsubscribe() {
FILE: src/packages/recompose/__tests__/getDisplayName-test.js
class SomeComponent (line 5) | class SomeComponent extends React.Component {
method render (line 6) | render() {
class SomeOtherComponent (line 11) | class SomeOtherComponent extends React.Component {
method render (line 13) | render() {
function YetAnotherComponent (line 18) | function YetAnotherComponent() {
FILE: src/packages/recompose/__tests__/isClassComponent-test.js
class Foo (line 12) | class Foo extends Component {
method render (line 13) | render() {
method render (line 20) | render() {
FILE: src/packages/recompose/__tests__/lifecycle-test.js
method componentWillMount (line 7) | componentWillMount() {
FILE: src/packages/recompose/__tests__/shallowEqual-test.js
method foo (line 6) | get foo() {
FILE: src/packages/recompose/__tests__/toClass-test.js
class BaseComponent (line 8) | class BaseComponent extends React.Component {
method render (line 9) | render() {
class Provider (line 52) | class Provider extends React.Component {
method render (line 57) | render() {
FILE: src/packages/recompose/__tests__/utils.js
class CountRenders (line 6) | class CountRenders extends React.Component {
method render (line 9) | render() {
FILE: src/packages/recompose/__tests__/withContext-test.js
class BaseProvider (line 17) | class BaseProvider extends Component {
method render (line 22) | render() {
FILE: src/packages/recompose/__tests__/withReducer-test.js
constant SET_COUNTER (line 6) | const SET_COUNTER = 'SET_COUNTER'
FILE: src/packages/recompose/__tests__/withStateHandlers-test.js
method persist (line 31) | persist() {
FILE: src/packages/recompose/__tests__/wrapDisplayName-test.js
class SomeComponent (line 5) | class SomeComponent extends React.Component {
method render (line 6) | render() {
FILE: src/packages/recompose/baconObservableConfig.js
method [$$observable] (line 27) | [$$observable]() {
FILE: src/packages/recompose/componentFromStream.js
method [$$observable] (line 24) | [$$observable]() {
method componentWillMount (line 32) | componentWillMount() {
method componentWillReceiveProps (line 42) | componentWillReceiveProps(nextProps) {
method shouldComponentUpdate (line 47) | shouldComponentUpdate(nextProps, nextState) {
method componentWillUnmount (line 51) | componentWillUnmount() {
method render (line 59) | render() {
FILE: src/packages/recompose/createEventHandler.js
method subscribe (line 8) | subscribe(observer) {
method [$$observable] (line 12) | [$$observable]() {
FILE: src/packages/recompose/createSink.js
class Sink (line 5) | class Sink extends Component {
method getDerivedStateFromProps (line 8) | static getDerivedStateFromProps(nextProps) {
method render (line 13) | render() {
FILE: src/packages/recompose/flydObservableConfig.js
method [$$observable] (line 27) | [$$observable]() {
FILE: src/packages/recompose/lifecycle.js
class Lifecycle (line 16) | class Lifecycle extends Component {
method render (line 17) | render() {
FILE: src/packages/recompose/mapPropsStream.js
method subscribe (line 19) | subscribe(observer) {
method [$$observable] (line 29) | [$$observable]() {
FILE: src/packages/recompose/renderNothing.js
class Nothing (line 3) | class Nothing extends Component {
method render (line 4) | render() {
FILE: src/packages/recompose/rxjs4ObservableConfig.js
method [$$observable] (line 23) | [$$observable]() {
FILE: src/packages/recompose/shallowEqual.js
function is (line 19) | function is(x, y) {
function shallowEqual (line 36) | function shallowEqual(objA, objB) {
FILE: src/packages/recompose/shouldUpdate.js
class ShouldUpdate (line 7) | class ShouldUpdate extends Component {
method shouldComponentUpdate (line 8) | shouldComponentUpdate(nextProps) {
method render (line 12) | render() {
FILE: src/packages/recompose/toClass.js
method render (line 13) | render() {
FILE: src/packages/recompose/toRenderProps.js
function toRenderProps (line 1) | function toRenderProps(hoc) {
FILE: src/packages/recompose/withContext.js
class WithContext (line 7) | class WithContext extends Component {
method render (line 10) | render() {
FILE: src/packages/recompose/withHandlers.js
class WithHandlers (line 9) | class WithHandlers extends Component {
method render (line 30) | render() {
FILE: src/packages/recompose/withPropsOnChange.js
class WithPropsOnChange (line 19) | class WithPropsOnChange extends Component {
method getDerivedStateFromProps (line 25) | static getDerivedStateFromProps(nextProps, prevState) {
method render (line 38) | render() {
FILE: src/packages/recompose/withReducer.js
class WithReducer (line 14) | class WithReducer extends Component {
method initializeStateValue (line 19) | initializeStateValue() {
method render (line 36) | render() {
FILE: src/packages/recompose/withState.js
class WithState (line 11) | class WithState extends Component {
method render (line 28) | render() {
FILE: src/packages/recompose/withStateHandlers.js
class WithStateHandlers (line 9) | class WithStateHandlers extends Component {
method render (line 29) | render() {
FILE: src/packages/recompose/xstreamObservableConfig.js
method start (line 10) | start(listener) {
method stop (line 13) | stop() {
method [$$observable] (line 29) | [$$observable]() {
Condensed preview — 170 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (237K chars).
[
{
"path": ".codeclimate.yml",
"chars": 213,
"preview": "---\nengines:\n duplication:\n enabled: true\n config:\n languages:\n - javascript\n eslint:\n enabled: tru"
},
{
"path": ".editorconfig",
"chars": 238,
"preview": "# editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_"
},
{
"path": ".eslintignore",
"chars": 24,
"preview": "**/node_modules\n**/types"
},
{
"path": ".eslintrc",
"chars": 1533,
"preview": "{\n \"extends\": [\n \"eslint-config-airbnb\",\n \"prettier\",\n \"prettier/flowtype\",\n \"prettier/react\"\n ],\n \"plugi"
},
{
"path": ".flowconfig",
"chars": 116,
"preview": "[ignore]\n<PROJECT_ROOT>/types/.*\n\n[include]\n\n[libs]\n\n[options]\nsuppress_comment=\\\\(.\\\\|\\n\\\\)*\\\\$ExpectError\n\n[lints]"
},
{
"path": ".gitignore",
"chars": 57,
"preview": "node_modules\nrelease\nlib\ncoverage\n.vscode\nyarn-error.log\n"
},
{
"path": ".npmignore",
"chars": 28,
"preview": "/**/__tests__\ncoverage\ntypes"
},
{
"path": ".prettierignore",
"chars": 25,
"preview": "node_modules\npackage.json"
},
{
"path": ".prettierrc",
"chars": 48,
"preview": "semi: false\nsingleQuote: true\ntrailingComma: es5"
},
{
"path": ".size-snapshot.json",
"chars": 525,
"preview": "{\n \"lib/packages/recompose/dist/Recompose.umd.js\": {\n \"bundled\": 46425,\n \"minified\": 16484,\n \"gzipped\": 4625\n "
},
{
"path": ".travis.yml",
"chars": 384,
"preview": "language: node_js\nnode_js:\n - \"node\"\ninstall: \"yarn install --ignore-engines\"\nbefore_script: \"npm install -g codeclimat"
},
{
"path": "CHANGELOG.md",
"chars": 105,
"preview": "We are using the [Github Releases page](https://github.com/acdlite/recompose/releases) as our CHANGELOG.\n"
},
{
"path": "LICENSE.md",
"chars": 1084,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015-2018 Andrew Clark\n\nPermission is hereby granted, free of charge, to any person"
},
{
"path": "README.md",
"chars": 12167,
"preview": "## A Note from the Author (acdlite, Oct 25 2018):\n\nHi! I created Recompose about three years ago. About a year after tha"
},
{
"path": "babel.config.js",
"chars": 250,
"preview": "module.exports = {\n plugins: [['@babel/proposal-class-properties', { loose: true }]],\n presets: [['@babel/env', { loos"
},
{
"path": "docs/API.md",
"chars": 32861,
"preview": "# API\n\nIn these API docs, a **higher-order component** (HOC) refers to a function that accepts a single React component "
},
{
"path": "docs/flow.md",
"chars": 2658,
"preview": "# Flow support for recompose\n\n## How it works\n\nIn most cases all you need is to declare a props type of enhanced Compone"
},
{
"path": "docs/performance.md",
"chars": 1037,
"preview": "# Should I use this? Performance and other concerns\n\nIf function composition doesn't scare you, then yes, I think so. I "
},
{
"path": "package.json",
"chars": 2903,
"preview": "{\n \"name\": \"recompose-build\",\n \"private\": true,\n \"author\": \"Andrew Clark <acdlite@me.com>\",\n \"repository\": {\n \"ty"
},
{
"path": "scripts/getPackageNames.js",
"chars": 554,
"preview": "const fs = require('fs')\nconst path = require('path')\n\nexports.PACKAGES_SRC_DIR = './src/packages'\nexports.PACKAGES_OUT_"
},
{
"path": "scripts/installNestedPackageDeps.js",
"chars": 340,
"preview": "const path = require('path')\nconst { exec } = require('shelljs')\nconst { getPackageNames, PACKAGES_SRC_DIR } = require('"
},
{
"path": "scripts/jest.setup.js",
"chars": 183,
"preview": "/* eslint-disable */\njasmine.DEFAULT_TIMEOUT_INTERVAL = 20000\n\nimport Enzyme from 'enzyme'\nimport Adapter from 'enzyme-a"
},
{
"path": "scripts/release.js",
"chars": 4479,
"preview": "/* eslint global-require: 0 */\n/* eslint-disable import/no-dynamic-require, no-console */\nconst fs = require('fs')\nconst"
},
{
"path": "scripts/rollup.config.js",
"chars": 2317,
"preview": "import path from 'path'\nimport nodeResolve from 'rollup-plugin-node-resolve'\nimport babel from 'rollup-plugin-babel'\nimp"
},
{
"path": "src/basePackage.json",
"chars": 292,
"preview": "{\n \"author\": \"Andrew Clark <acdlite@me.com>\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/acdli"
},
{
"path": "src/packages/recompose/.npmignore",
"chars": 14,
"preview": "/**/__tests__\n"
},
{
"path": "src/packages/recompose/README.md",
"chars": 319,
"preview": "recompose\n=========\n\n[](https://www.npmjs.co"
},
{
"path": "src/packages/recompose/VERSION",
"chars": 7,
"preview": "0.30.0\n"
},
{
"path": "src/packages/recompose/__tests__/branch-test.js",
"chars": 1927,
"preview": "import sinon from 'sinon'\nimport React from 'react'\nimport { mount } from 'enzyme'\nimport { branch, compose, withState, "
},
{
"path": "src/packages/recompose/__tests__/componentFromProp-test.js",
"chars": 579,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport { componentFromProp } from '../'\n\ntest('componentFromPro"
},
{
"path": "src/packages/recompose/__tests__/componentFromStream-test.js",
"chars": 3055,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport { Observable, Subject } from 'rxjs'\nimport sinon from 's"
},
{
"path": "src/packages/recompose/__tests__/componentFromStreamWithConfig-test.js",
"chars": 884,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport { Observable } from 'rxjs'\nimport { Stream as MostStream"
},
{
"path": "src/packages/recompose/__tests__/compose-test.js",
"chars": 768,
"preview": "import { compose } from '../'\n\ntest('compose composes from right to left', () => {\n const double = x => x * 2\n const s"
},
{
"path": "src/packages/recompose/__tests__/createEventHandler-test.js",
"chars": 844,
"preview": "import { createEventHandler } from '../'\n\ntest('createEventHandler creates an event handler and a corresponding stream',"
},
{
"path": "src/packages/recompose/__tests__/createSink-test.js",
"chars": 787,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport { createSink, compose, withSta"
},
{
"path": "src/packages/recompose/__tests__/defaultProps-test.js",
"chars": 1076,
"preview": "import React from 'react'\nimport { shallow } from 'enzyme'\nimport { defaultProps } from '../'\n\ntest('defaultProps passes"
},
{
"path": "src/packages/recompose/__tests__/fixtures/treeshake-entry.js",
"chars": 63,
"preview": "import '../../../../../lib/packages/recompose/es/Recompose.js'\n"
},
{
"path": "src/packages/recompose/__tests__/flattenProp-test.js",
"chars": 748,
"preview": "import React from 'react'\nimport { shallow } from 'enzyme'\nimport { flattenProp } from '../'\n\ntest('flattenProps flatten"
},
{
"path": "src/packages/recompose/__tests__/fromRenderProps-test.js",
"chars": 2751,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport { fromRenderProps, compose, toRenderProps, defaultProps "
},
{
"path": "src/packages/recompose/__tests__/getContext-test.js",
"chars": 170,
"preview": "// Tests for getContext() are covered by withContext-test.js\n// This is just a dummy test so Ava doesn't complain\ntest('"
},
{
"path": "src/packages/recompose/__tests__/getDisplayName-test.js",
"chars": 749,
"preview": "import React from 'react'\nimport { getDisplayName } from '../'\n\ntest('getDisplayName gets the display name of a React co"
},
{
"path": "src/packages/recompose/__tests__/hoistStatics-test.js",
"chars": 991,
"preview": "import React, { createFactory } from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport { hoistStat"
},
{
"path": "src/packages/recompose/__tests__/isClassComponent-test.js",
"chars": 696,
"preview": "import React, { Component } from 'react'\nimport createReactClass from 'create-react-class'\nimport isClassComponent from "
},
{
"path": "src/packages/recompose/__tests__/lifecycle-test.js",
"chars": 507,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport { lifecycle } from '../'\n\ntest('lifecycle is a higher-or"
},
{
"path": "src/packages/recompose/__tests__/mapProps-test.js",
"chars": 812,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport { mapProps, withState, compose"
},
{
"path": "src/packages/recompose/__tests__/mapPropsStream-test.js",
"chars": 671,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport setObservableConfig from '../setObservableConfig'\nimport"
},
{
"path": "src/packages/recompose/__tests__/mapPropsStreamWithConfig-test.js",
"chars": 1248,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport { Stream as MostStream } from 'most'\nimport { Observable"
},
{
"path": "src/packages/recompose/__tests__/nest-test.js",
"chars": 625,
"preview": "import React from 'react'\nimport { shallow } from 'enzyme'\nimport { nest, setDisplayName, toClass } from '../'\n\ntest('ne"
},
{
"path": "src/packages/recompose/__tests__/onlyUpdateForKeys-test.js",
"chars": 1040,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport { onlyUpdateForKeys, compose, "
},
{
"path": "src/packages/recompose/__tests__/onlyUpdateForPropTypes-test.js",
"chars": 1672,
"preview": "import React from 'react'\nimport PropTypes from 'prop-types'\nimport sinon from 'sinon'\nimport { mount, shallow } from 'e"
},
{
"path": "src/packages/recompose/__tests__/pure-test.js",
"chars": 1080,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport { pure, compose, withState } f"
},
{
"path": "src/packages/recompose/__tests__/renameProp-test.js",
"chars": 482,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport { withProps, renameProp, compose } from '../'\n\ntest('ren"
},
{
"path": "src/packages/recompose/__tests__/renameProps-test.js",
"chars": 519,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport { withProps, renameProps, compose } from '../'\n\ntest('re"
},
{
"path": "src/packages/recompose/__tests__/renderComponent-test.js",
"chars": 768,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport { renderComponent, withState, "
},
{
"path": "src/packages/recompose/__tests__/renderNothing-test.js",
"chars": 413,
"preview": "import React from 'react'\nimport { shallow } from 'enzyme'\nimport { renderNothing } from '../'\n\ntest('renderNothing retu"
},
{
"path": "src/packages/recompose/__tests__/setDisplayName-test.js",
"chars": 288,
"preview": "import React from 'react'\nimport { setDisplayName } from '../'\n\ntest('setDisplayName sets a static property on the base "
},
{
"path": "src/packages/recompose/__tests__/setObservableConfig-test.js",
"chars": 2017,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport rxjs5Config from '../rxjsObservableConfig'\nimport rxjs4C"
},
{
"path": "src/packages/recompose/__tests__/setPropTypes-test.js",
"chars": 366,
"preview": "import React from 'react'\nimport PropTypes from 'prop-types'\nimport { setPropTypes } from '../'\n\ntest('setPropTypes sets"
},
{
"path": "src/packages/recompose/__tests__/setStatic-test.js",
"chars": 378,
"preview": "import React from 'react'\nimport PropTypes from 'prop-types'\nimport { setStatic } from '../'\n\ntest('setStatic sets a sta"
},
{
"path": "src/packages/recompose/__tests__/shallowEqual-test.js",
"chars": 1444,
"preview": "import { shallowEqual } from '../'\n\n// Adapted from https://github.com/rackt/react-redux/blob/master/test/utils/shallowE"
},
{
"path": "src/packages/recompose/__tests__/shouldUpdate-test.js",
"chars": 1152,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport { shouldUpdate, compose, withS"
},
{
"path": "src/packages/recompose/__tests__/toClass-test.js",
"chars": 2692,
"preview": "import React from 'react'\nimport PropTypes from 'prop-types'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nim"
},
{
"path": "src/packages/recompose/__tests__/toRenderProps-test.js",
"chars": 520,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport { toRenderProps, defaultProps } from '../'\n\ntest('toRend"
},
{
"path": "src/packages/recompose/__tests__/types/test_branch.js",
"chars": 2900,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flow */\nimport React from 'react'\nimpor"
},
{
"path": "src/packages/recompose/__tests__/types/test_classBasedEnhancer.js",
"chars": 1251,
"preview": "/* @flow */\nimport * as React from 'react'\nimport { compose, withProps } from '../..'\nimport type { HOC } from '../..'\n\n"
},
{
"path": "src/packages/recompose/__tests__/types/test_componentFromStream.js",
"chars": 243,
"preview": "// @flow\nimport React from 'react'\nimport { componentFromStream } from '../..'\n\n// $ExpectError\ncomponentFromStream(1)\n\n"
},
{
"path": "src/packages/recompose/__tests__/types/test_createEventHandler.js",
"chars": 308,
"preview": "// @flow\nimport React from 'react'\nimport { createEventHandler } from '../..'\n\n// $ExpectError\ncreateEventHandler(1)\n\n//"
},
{
"path": "src/packages/recompose/__tests__/types/test_defaultProps.js",
"chars": 955,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions */\n/* @flow */\nimport React from 'react'\nimport { compose, withP"
},
{
"path": "src/packages/recompose/__tests__/types/test_fromRenderProps.js",
"chars": 1265,
"preview": "/* @flow */\nimport React from 'react'\nimport { compose, fromRenderProps } from '../..'\n\nimport type { HOC } from '../..'"
},
{
"path": "src/packages/recompose/__tests__/types/test_functionalEnhancer.js",
"chars": 900,
"preview": "/* @flow */\n\nimport * as React from 'react'\nimport { compose, withProps } from '../..'\nimport type { HOC } from '../..'\n"
},
{
"path": "src/packages/recompose/__tests__/types/test_getContext.js",
"chars": 1111,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flow */\nimport React from 'react'\nimpor"
},
{
"path": "src/packages/recompose/__tests__/types/test_mapProps.js",
"chars": 770,
"preview": "/* globals */\n/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flow */\nimport React from"
},
{
"path": "src/packages/recompose/__tests__/types/test_mapPropsStream.js",
"chars": 1019,
"preview": "/* globals */\n/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flow */\nimport React from"
},
{
"path": "src/packages/recompose/__tests__/types/test_onlyUpdateForKeys.js",
"chars": 843,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions */\n/* @flow */\nimport React from 'react'\nimport { compose, withP"
},
{
"path": "src/packages/recompose/__tests__/types/test_onlyUpdateForPropTypes.js",
"chars": 722,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions */\n/* @flow */\nimport React from 'react'\nimport { compose, withP"
},
{
"path": "src/packages/recompose/__tests__/types/test_pure.js",
"chars": 686,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions */\n/* @flow */\nimport React from 'react'\nimport { compose, withP"
},
{
"path": "src/packages/recompose/__tests__/types/test_shouldUpdate.js",
"chars": 1058,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flow */\nimport React from 'react'\nimpor"
},
{
"path": "src/packages/recompose/__tests__/types/test_statics.js",
"chars": 1085,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flow */\nimport React from 'react'\nimpor"
},
{
"path": "src/packages/recompose/__tests__/types/test_toClass.js",
"chars": 692,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions */\n/* @flow */\nimport React from 'react'\nimport { compose, withP"
},
{
"path": "src/packages/recompose/__tests__/types/test_toRenderProps.js",
"chars": 1193,
"preview": "/* @flow */\n\nimport * as React from 'react'\nimport { compose, withProps, toRenderProps, withHandlers } from '../..'\nimpo"
},
{
"path": "src/packages/recompose/__tests__/types/test_utils.js",
"chars": 667,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flow */\n\nimport React from 'react'\nimpo"
},
{
"path": "src/packages/recompose/__tests__/types/test_voodoo.js",
"chars": 3101,
"preview": "/* globals $Exact, $PropertyType */\n/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flo"
},
{
"path": "src/packages/recompose/__tests__/types/test_withContext.js",
"chars": 818,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flow */\nimport React from 'react'\nimpor"
},
{
"path": "src/packages/recompose/__tests__/types/test_withHandlers.js",
"chars": 1931,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions */\n/* @flow */\n\nimport React from 'react'\nimport { compose, with"
},
{
"path": "src/packages/recompose/__tests__/types/test_withProps.js",
"chars": 1233,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions */\n/* @flow */\nimport React from 'react'\nimport { compose, withP"
},
{
"path": "src/packages/recompose/__tests__/types/test_withPropsOnChange.js",
"chars": 2143,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions, arrow-body-style */\n/* @flow */\nimport React from 'react'\nimpor"
},
{
"path": "src/packages/recompose/__tests__/types/test_withStateHandlers.js",
"chars": 2850,
"preview": "/* eslint-disable no-unused-vars, no-unused-expressions */\n/* @flow */\n\nimport React from 'react'\nimport { compose, with"
},
{
"path": "src/packages/recompose/__tests__/utils.js",
"chars": 466,
"preview": "import React from 'react'\nimport setDisplayName from '../setDisplayName'\nimport wrapDisplayName from '../wrapDisplayName"
},
{
"path": "src/packages/recompose/__tests__/withContext-test.js",
"chars": 1352,
"preview": "/* eslint-disable react/require-default-props */\nimport React, { Component } from 'react'\nimport PropTypes from 'prop-ty"
},
{
"path": "src/packages/recompose/__tests__/withHandlers-test.js",
"chars": 3083,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport { withHandlers, withState, com"
},
{
"path": "src/packages/recompose/__tests__/withProps-test.js",
"chars": 932,
"preview": "import React from 'react'\nimport { shallow } from 'enzyme'\nimport { withProps } from '../'\n\ntest('withProps passes addit"
},
{
"path": "src/packages/recompose/__tests__/withPropsOnChange-test.js",
"chars": 3770,
"preview": "import * as React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport {\n withPropsOnChange,\n "
},
{
"path": "src/packages/recompose/__tests__/withReducer-test.js",
"chars": 2885,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\nimport { withReducer, compose, flatte"
},
{
"path": "src/packages/recompose/__tests__/withState-test.js",
"chars": 2160,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\n\nimport { withState } from '../'\n\ntes"
},
{
"path": "src/packages/recompose/__tests__/withStateHandlers-test.js",
"chars": 5924,
"preview": "import React from 'react'\nimport { mount } from 'enzyme'\nimport sinon from 'sinon'\n\nimport { compose, withStateHandlers "
},
{
"path": "src/packages/recompose/__tests__/wrapDisplayName-test.js",
"chars": 367,
"preview": "import React from 'react'\nimport { wrapDisplayName } from '../'\n\ntest('wrapDisplayName wraps the display name of a React"
},
{
"path": "src/packages/recompose/baconObservableConfig.js",
"chars": 857,
"preview": "import $$observable from 'symbol-observable'\nimport Bacon from 'baconjs'\n\nconst config = {\n fromESObservable: observabl"
},
{
"path": "src/packages/recompose/branch.js",
"chars": 713,
"preview": "import { createFactory } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName from './wrapD"
},
{
"path": "src/packages/recompose/componentFromProp.js",
"chars": 314,
"preview": "import { createElement } from 'react'\nimport omit from './utils/omit'\n\nconst componentFromProp = propName => {\n const C"
},
{
"path": "src/packages/recompose/componentFromStream.js",
"chars": 1765,
"preview": "import { Component } from 'react'\nimport { createChangeEmitter } from 'change-emitter'\nimport $$observable from 'symbol-"
},
{
"path": "src/packages/recompose/compose.js",
"chars": 119,
"preview": "const compose = (...funcs) =>\n funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg)\n\nexport default compose\n"
},
{
"path": "src/packages/recompose/createEventHandler.js",
"chars": 656,
"preview": "import $$observable from 'symbol-observable'\nimport { createChangeEmitter } from 'change-emitter'\nimport { config as glo"
},
{
"path": "src/packages/recompose/createSink.js",
"chars": 372,
"preview": "import { Component } from 'react'\nimport { polyfill } from 'react-lifecycles-compat'\n\nconst createSink = callback => {\n "
},
{
"path": "src/packages/recompose/defaultProps.js",
"chars": 526,
"preview": "import { createFactory } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName from './wrapD"
},
{
"path": "src/packages/recompose/flattenProp.js",
"chars": 526,
"preview": "import { createFactory } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName from './wrapD"
},
{
"path": "src/packages/recompose/flydObservableConfig.js",
"chars": 733,
"preview": "import $$observable from 'symbol-observable'\nimport flyd from 'flyd'\n\nconst noop = () => {}\n\nconst config = {\n fromESOb"
},
{
"path": "src/packages/recompose/fromRenderProps.js",
"chars": 763,
"preview": "import React from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName from './wrapDisplayName'\n"
},
{
"path": "src/packages/recompose/getContext.js",
"chars": 579,
"preview": "import { createFactory } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName from './wrapD"
},
{
"path": "src/packages/recompose/getDisplayName.js",
"chars": 246,
"preview": "const getDisplayName = Component => {\n if (typeof Component === 'string') {\n return Component\n }\n\n if (!Component)"
},
{
"path": "src/packages/recompose/hoistStatics.js",
"chars": 312,
"preview": "import hoistNonReactStatics from 'hoist-non-react-statics'\n\nconst hoistStatics = (higherOrderComponent, blacklist) => Ba"
},
{
"path": "src/packages/recompose/index.js",
"chars": 2534,
"preview": "// Higher-order component helpers\nexport { default as mapProps } from './mapProps'\nexport { default as withProps } from "
},
{
"path": "src/packages/recompose/index.js.flow",
"chars": 9223,
"preview": "/* eslint-disable */\n\n/* @flow strict */\n\n/**\n * 1) Types give additional constraint on a language, recompose was writte"
},
{
"path": "src/packages/recompose/isClassComponent.js",
"chars": 187,
"preview": "const isClassComponent = Component =>\n Boolean(\n Component &&\n Component.prototype &&\n typeof Component.pr"
},
{
"path": "src/packages/recompose/kefirObservableConfig.js",
"chars": 166,
"preview": "import Kefir from 'kefir'\n\nconst config = {\n fromESObservable: Kefir.fromESObservable,\n toESObservable: stream => stre"
},
{
"path": "src/packages/recompose/lifecycle.js",
"chars": 928,
"preview": "/* eslint-disable no-console */\nimport { createFactory, Component } from 'react'\nimport setDisplayName from './setDispla"
},
{
"path": "src/packages/recompose/mapProps.js",
"chars": 463,
"preview": "import { createFactory } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName from './wrapD"
},
{
"path": "src/packages/recompose/mapPropsStream.js",
"chars": 1422,
"preview": "import { createFactory } from 'react'\nimport $$observable from 'symbol-observable'\nimport { componentFromStreamWithConfi"
},
{
"path": "src/packages/recompose/mostObservableConfig.js",
"chars": 156,
"preview": "import { from, Stream } from 'most'\n\nconst config = {\n fromESObservable: from || Stream.from,\n toESObservable: stream "
},
{
"path": "src/packages/recompose/nest.js",
"chars": 495,
"preview": "import { createFactory } from 'react'\nimport getDisplayName from './getDisplayName'\n\nconst nest = (...Components) => {\n "
},
{
"path": "src/packages/recompose/onlyUpdateForKeys.js",
"chars": 618,
"preview": "import shouldUpdate from './shouldUpdate'\nimport shallowEqual from './shallowEqual'\nimport setDisplayName from './setDis"
},
{
"path": "src/packages/recompose/onlyUpdateForPropTypes.js",
"chars": 1033,
"preview": "import onlyUpdateForKeys from './onlyUpdateForKeys'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName"
},
{
"path": "src/packages/recompose/package.json",
"chars": 598,
"preview": "{\n \"description\": \"A React utility belt for function components and higher-order components\",\n \"keywords\": [\n \"reac"
},
{
"path": "src/packages/recompose/pure.js",
"chars": 503,
"preview": "import shouldUpdate from './shouldUpdate'\nimport shallowEqual from './shallowEqual'\nimport setDisplayName from './setDis"
},
{
"path": "src/packages/recompose/renameProp.js",
"chars": 531,
"preview": "import omit from './utils/omit'\nimport mapProps from './mapProps'\nimport setDisplayName from './setDisplayName'\nimport w"
},
{
"path": "src/packages/recompose/renameProps.js",
"chars": 871,
"preview": "import omit from './utils/omit'\nimport pick from './utils/pick'\nimport mapProps from './mapProps'\nimport setDisplayName "
},
{
"path": "src/packages/recompose/renderComponent.js",
"chars": 414,
"preview": "import { createFactory } from 'react'\nimport wrapDisplayName from './wrapDisplayName'\n\nconst renderComponent = Component"
},
{
"path": "src/packages/recompose/renderNothing.js",
"chars": 170,
"preview": "import { Component } from 'react'\n\nclass Nothing extends Component {\n render() {\n return null\n }\n}\n\nconst renderNot"
},
{
"path": "src/packages/recompose/rxjs4ObservableConfig.js",
"chars": 770,
"preview": "import $$observable from 'symbol-observable'\nimport Rx from 'rx'\n\nconst config = {\n fromESObservable: observable =>\n "
},
{
"path": "src/packages/recompose/rxjsObservableConfig.js",
"chars": 141,
"preview": "import Rx from 'rxjs'\n\nconst config = {\n fromESObservable: Rx.Observable.from,\n toESObservable: stream => stream,\n}\n\ne"
},
{
"path": "src/packages/recompose/setDisplayName.js",
"chars": 144,
"preview": "import setStatic from './setStatic'\n\nconst setDisplayName = displayName => setStatic('displayName', displayName)\n\nexport"
},
{
"path": "src/packages/recompose/setObservableConfig.js",
"chars": 465,
"preview": "let _config = {\n fromESObservable: null,\n toESObservable: null,\n}\n\nconst configureObservable = c => {\n _config = c\n}\n"
},
{
"path": "src/packages/recompose/setPropTypes.js",
"chars": 134,
"preview": "import setStatic from './setStatic'\n\nconst setPropTypes = propTypes => setStatic('propTypes', propTypes)\n\nexport default"
},
{
"path": "src/packages/recompose/setStatic.js",
"chars": 214,
"preview": "const setStatic = (key, value) => BaseComponent => {\n /* eslint-disable no-param-reassign */\n BaseComponent[key] = val"
},
{
"path": "src/packages/recompose/shallowEqual.js",
"chars": 1647,
"preview": "/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n"
},
{
"path": "src/packages/recompose/shouldUpdate.js",
"chars": 630,
"preview": "import { createFactory, Component } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName fr"
},
{
"path": "src/packages/recompose/toClass.js",
"chars": 735,
"preview": "import React, { Component } from 'react'\nimport getDisplayName from './getDisplayName'\nimport isClassComponent from './i"
},
{
"path": "src/packages/recompose/toRenderProps.js",
"chars": 144,
"preview": "export default function toRenderProps(hoc) {\n const RenderPropsComponent = props => props.children(props)\n return hoc("
},
{
"path": "src/packages/recompose/utils/mapValues.js",
"chars": 298,
"preview": "const mapValues = (obj, func) => {\n const result = {}\n /* eslint-disable no-restricted-syntax */\n for (const key in o"
},
{
"path": "src/packages/recompose/utils/omit.js",
"chars": 228,
"preview": "const omit = (obj, keys) => {\n const { ...rest } = obj\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i"
},
{
"path": "src/packages/recompose/utils/pick.js",
"chars": 229,
"preview": "const pick = (obj, keys) => {\n const result = {}\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i]\n "
},
{
"path": "src/packages/recompose/withContext.js",
"chars": 679,
"preview": "import { createFactory, Component } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName fr"
},
{
"path": "src/packages/recompose/withHandlers.js",
"chars": 1237,
"preview": "/* eslint-disable no-console */\nimport { createFactory, Component } from 'react'\nimport setDisplayName from './setDispla"
},
{
"path": "src/packages/recompose/withProps.js",
"chars": 496,
"preview": "import wrapDisplayName from './wrapDisplayName'\nimport setDisplayName from './setDisplayName'\nimport mapProps from './ma"
},
{
"path": "src/packages/recompose/withPropsOnChange.js",
"chars": 1444,
"preview": "import { createFactory, Component } from 'react'\nimport { polyfill } from 'react-lifecycles-compat'\nimport pick from './"
},
{
"path": "src/packages/recompose/withReducer.js",
"chars": 1259,
"preview": "import { createFactory, Component } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName fr"
},
{
"path": "src/packages/recompose/withState.js",
"chars": 1071,
"preview": "import { createFactory, Component } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName fr"
},
{
"path": "src/packages/recompose/withStateHandlers.js",
"chars": 1261,
"preview": "import { createFactory, Component } from 'react'\nimport setDisplayName from './setDisplayName'\nimport wrapDisplayName fr"
},
{
"path": "src/packages/recompose/wrapDisplayName.js",
"chars": 180,
"preview": "import getDisplayName from './getDisplayName'\n\nconst wrapDisplayName = (BaseComponent, hocName) =>\n `${hocName}(${getDi"
},
{
"path": "src/packages/recompose/xstreamObservableConfig.js",
"chars": 779,
"preview": "import $$observable from 'symbol-observable'\nimport xstream from 'xstream'\n\nconst noop = () => {}\n\nconst config = {\n fr"
},
{
"path": "src/packages/recompose-relay/.npmignore",
"chars": 14,
"preview": "/**/__tests__\n"
},
{
"path": "src/packages/recompose-relay/README.md",
"chars": 1377,
"preview": "recompose-relay\n===============\n\n[](ht"
},
{
"path": "src/packages/recompose-relay/VERSION",
"chars": 6,
"preview": "0.3.1\n"
},
{
"path": "src/packages/recompose-relay/createContainer.js",
"chars": 210,
"preview": "import Relay from 'react-relay'\nimport { toClass } from 'recompose'\n\nconst createContainer = options => BaseComponent =>"
},
{
"path": "src/packages/recompose-relay/index.js",
"chars": 63,
"preview": "export { default as createContainer } from './createContainer'\n"
},
{
"path": "src/packages/recompose-relay/package.json",
"chars": 622,
"preview": "{\n \"description\": \"Recompose helpers for Relay.\",\n \"scripts\": {\n \"update-schema\": \"node -r babel-core/register upda"
},
{
"path": "types/README.md",
"chars": 3041,
"preview": "# Flow support for recompose\n\n## How it works\n\nIn most cases all you need is to declare a props type of enhanced Compone"
},
{
"path": "types/flow-example/.eslintrc",
"chars": 29,
"preview": "{\n \"extends\": \"react-app\"\n}\n"
},
{
"path": "types/flow-example/.flowconfig",
"chars": 310,
"preview": "[ignore]\n.*/node_modules/eslint-plugin-jsx-a11y/.*\n# TODO: remove after react-motion will be updated with new flow typed"
},
{
"path": "types/flow-example/.gitignore",
"chars": 285,
"preview": "# See https://help.github.com/ignore-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n# testing\n/cov"
},
{
"path": "types/flow-example/README.md",
"chars": 147,
"preview": "# recompose with flow usage example\n\nThis project was bootstrapped with [Create React App](https://github.com/facebookin"
},
{
"path": "types/flow-example/flow-typed/glamor.js",
"chars": 274,
"preview": "declare module 'glamor' {\n declare type CSSF = (...style: Object[]) => Object\n declare type CSS = {\n insert: (css: "
},
{
"path": "types/flow-example/flow-typed/react-motion.js",
"chars": 289,
"preview": "// TODO: remove after react-motion will be updated with new flow typedefs\ndeclare module 'react-motion' {\n declare expo"
},
{
"path": "types/flow-example/package.json",
"chars": 549,
"preview": "{\n \"name\": \"coolmenu\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"dependencies\": {\n \"change-emitter\": \"^0.1.6\",\n "
},
{
"path": "types/flow-example/public/index.html",
"chars": 1567,
"preview": "<!doctype html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, i"
},
{
"path": "types/flow-example/public/manifest.json",
"chars": 298,
"preview": "{\n \"short_name\": \"React App\",\n \"name\": \"Create React App Sample\",\n \"icons\": [\n {\n \"src\": \"favicon.ico\",\n "
},
{
"path": "types/flow-example/src/App.js",
"chars": 2298,
"preview": "/* @flow */\nimport React from 'react'\nimport { css } from 'glamor'\nimport { compose, defaultProps } from 'recompose'\nimp"
},
{
"path": "types/flow-example/src/Item.js",
"chars": 2893,
"preview": "/* @flow */\n\nimport React from 'react'\nimport { css } from 'glamor'\nimport { compose, defaultProps, withProps, withProps"
},
{
"path": "types/flow-example/src/ItemsAnimator.js",
"chars": 4193,
"preview": "/* @flow */\n\nimport React from 'react'\nimport { css } from 'glamor'\nimport { compose, defaultProps, withHandlers, withPr"
},
{
"path": "types/flow-example/src/MouseDetector.js",
"chars": 4103,
"preview": "/* @flow */\nimport React from 'react'\nimport {\n compose,\n defaultProps,\n withHandlers,\n withProps,\n withStateHandle"
},
{
"path": "types/flow-example/src/index.js",
"chars": 843,
"preview": "/* @flow */\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport App from './App'\nimport { css } from 'glam"
}
]
About this extraction
This page contains the full source code of the acdlite/recompose GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 170 files (211.5 KB), approximately 60.1k tokens, and a symbol index with 69 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.