-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Paul Klimashkin
committed
May 18, 2018
1 parent
429ac92
commit d52d620
Showing
8 changed files
with
372 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
root = true | ||
|
||
[*] | ||
charset = utf-8 | ||
|
||
indent_style = space | ||
indent_size = 2 | ||
|
||
end_of_line = lf | ||
insert_final_newline = false | ||
trim_trailing_whitespace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# OS-specific files | ||
.DS_Store | ||
Thumbs.db | ||
|
||
# IntelliJ | ||
/.idea/ | ||
# VSCode | ||
/.vscode/ | ||
|
||
# Dependency directories | ||
node_modules/ | ||
jspm_packages/ | ||
|
||
# dist folder, users can get build files on unpkg | ||
dist/ | ||
|
||
# Optional npm cache directory | ||
.npm | ||
|
||
# Output of 'npm pack' | ||
*.tgz | ||
|
||
# Yarn Integrity file | ||
.yarn-integrity | ||
|
||
# Directory for instrumented libs generated by jscoverage/JSCover | ||
lib-cov | ||
|
||
# Optional REPL history | ||
.node_repl_history | ||
# dotenv environment variables file | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package-lock=false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,82 @@ | ||
# react-forwardref-utils | ||
Utils to help with React 16.3+ forwardRef method | ||
Utils to help with React 16.3+ [forwardRef](https://reactjs.org/docs/forwarding-refs.html) method | ||
|
||
```bash | ||
$ npm install --save react-forwardref-utils | ||
``` | ||
|
||
## Usage | ||
|
||
```js | ||
import { | ||
isForwardRef, forwardRefSymbol, forwardRefFactory, withForwardRef, | ||
} from 'react-forwardref-utils'; | ||
``` | ||
|
||
### `isForwardRef` | ||
Tests if Component is produced by forwardRef function | ||
|
||
```js | ||
import {isForwardRef} from 'react-forwardref-utils'; | ||
|
||
const FancyButton = React.forwardRef((props, ref) => ( | ||
<button ref={ref} className="FancyButton"> | ||
{props.children} | ||
</button> | ||
)); | ||
|
||
isForwardRef(FancyButton); // true | ||
|
||
``` | ||
|
||
### `forwardRefSymbol` | ||
Symbol (currently just a string) that points to the ref passed to a component that wrapped with `forwardRefFactory`. | ||
|
||
### `forwardRefFactory(Component, [options])` | ||
Wraps passed component with `forwardRef`, assigns its statics to result using [hoist-non-react-statics](https://github.com/mridgway/hoist-non-react-statics) and provide special symbol to take ref from parent call. | ||
|
||
```js | ||
import {forwardRefFactory, forwardRefSymbol} from 'react-forwardref-utils'; | ||
|
||
class Button extends React.Component { | ||
render () { | ||
const {[forwardRefSymbol]: ref, ...props} = this.props; | ||
|
||
props.ref = ref; | ||
props.className = "FancyButton"; | ||
|
||
return <button {...props}>{props.children}</button>; | ||
} | ||
} | ||
|
||
export default forwardRefFactory(Button/*, {displayName, hoistSource, hoistExclude}*/); | ||
``` | ||
###### Options: | ||
- `displayName` *(String)* Name of the result component that will be used in devtools. Will be taken from source component if omitted. | ||
- `hoistSource` *(Object|Function)* Source for taking static methods for assigning them to result component. In case you want to wrap with forwardRef another wrapper of original component, but methods should be copied from original. For instance, when you write HOC and wrap that HOC with forwardRefFactory - statics should be copied from original component, not from the HOC. | ||
- `hoistExclude` *(Object)* Object to exclude some methods from hoisting, third parameter of hoist-non-react-statics module. By default excludes forwardRef component properties in case we wrap another forwardRef component (until https://github.com/mridgway/hoist-non-react-statics/issues/48 is resolved). | ||
|
||
### `withForwardRef([options])` | ||
Decorator that wraps decorated component with `forwardRefFactory`. | ||
|
||
```js | ||
import {withForwardRef, forwardRefSymbol} from 'react-forwardref-utils'; | ||
|
||
@withForwardRef() | ||
export default class Button extends React.Component { | ||
render () { | ||
const {[forwardRefSymbol]: ref, ...props} = this.props; | ||
|
||
props.ref = ref; | ||
props.className = "FancyButton"; | ||
|
||
return <button {...props}>{props.children}</button>; | ||
} | ||
} | ||
``` | ||
|
||
|
||
## License | ||
[MIT](https://github.com/klimashkin/react-forwardref-utils/blob/master/LICENSE) | ||
|
||
[LICENSE file]: https://github.com/klimashkin/react-forwardref-utils/blob/master/LICENSE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
{ | ||
"name": "react-forwardref-utils", | ||
"version": "1.0.0", | ||
"description": "Utils to help with React 16.3+ forwardRef method", | ||
"main": "index.js", | ||
"author": "Pavel Klimashkin", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/klimashkin/react-forwardref-utils.git" | ||
}, | ||
"keywords": [ | ||
"react", | ||
"forwardref" | ||
], | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/klimashkin/react-forwardref-utils/issues" | ||
}, | ||
"homepage": "https://github.com/klimashkin/react-forwardref-utils#readme", | ||
"dependencies": { | ||
"hoist-non-react-statics": "^2.5.0" | ||
}, | ||
"peerDependencies": { | ||
"react": "^16.3.0" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.26.0", | ||
"babel-core": "^6.26.3", | ||
"babel-loader": "^7.1.4", | ||
"babel-plugin-syntax-async-functions": "^6.13.0", | ||
"babel-plugin-syntax-trailing-function-commas": "^6.22.0", | ||
"babel-plugin-transform-async-to-generator": "^6.24.1", | ||
"babel-plugin-transform-class-properties": "^6.24.1", | ||
"babel-plugin-transform-object-rest-spread": "^6.26.0", | ||
"babel-plugin-transform-react-remove-prop-types": "^0.4.13", | ||
"babel-preset-env": "^1.7.0", | ||
"babel-preset-react": "^6.24.1", | ||
"cross-env": "^5.1.4", | ||
"github-changes": "^1.1.2", | ||
"null-loader": "^0.1.1", | ||
"react": "^16.3.0", | ||
"react-dom": "^16.3.0", | ||
"rimraf": "^2.6.2", | ||
"uglifyjs-webpack-plugin": "^1.2.5", | ||
"webpack": "^4.8.3", | ||
"webpack-cli": "^2.1.3" | ||
}, | ||
"scripts": { | ||
"preversion": "npm run clean && npm run build", | ||
"postversion": "npm run changelog", | ||
"build:umd": "cross-env BUILD_MODE=umd webpack", | ||
"build:umd-min": "cross-env BUILD_MODE=umd-min webpack", | ||
"build:es6": "cross-env BUILD_MODE=es2015 webpack", | ||
"build:es6-min": "cross-env BUILD_MODE=es2015-min webpack", | ||
"build:es8": "cross-env BUILD_MODE=es2017 webpack", | ||
"build:es8-min": "cross-env BUILD_MODE=es2017-min webpack", | ||
"build": "npm run build:umd && npm run build:umd-min && npm run build:es6 && npm run build:es6-min && npm run build:es8 && npm run build:es8-min", | ||
"clean": "rimraf dist", | ||
"changelog": "github-changes -o klimashkin -r react-forwardref-utils -b master -f ./CHANGELOG.md --order-semver --use-commit-body" | ||
}, | ||
"engines": { | ||
"node": ">=6.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import React, {forwardRef} from 'react'; | ||
import hoistStatics from 'hoist-non-react-statics'; | ||
|
||
/** | ||
* Tests if Component is produced by forwardRef function | ||
* @param Component | ||
* @returns {boolean} | ||
*/ | ||
export const isForwardRef = Component => Component.$$typeof && typeof Component.render === 'function'; | ||
|
||
/** | ||
* Use just a string for now (react 16.3), since react doesn't support Symbols in props yet | ||
* https://github.com/facebook/react/issues/7552 | ||
* @type {string} | ||
*/ | ||
export const forwardRefSymbol = '__forwardRef__'; | ||
|
||
/** | ||
* Wraps passed component with react 'forwardRef' function, which produce new component with type 'object' and structure like so: | ||
* { $$type: Symbol(), render: function } | ||
* Assigns (hoists) static methods of passed component to result forward component using 'hoist-non-react-statics' module. | ||
* | ||
* @param {Object|Function} Component - Component to wrap with forwardRef | ||
* @param {Object} [options] - Optional parameters | ||
* @param {String} [options.displayName] - Name of the result component that will be used in devtools. | ||
* Will be taken from source component if omitted | ||
* @param {Object|Function} [options.hoistSource] - Source for taking static methods for assigning them to result component. | ||
* In case you want to wrap with forwardRef another wrapper of original component, | ||
* but methods should be copied from original | ||
* @param {Object} [options.hoistExclude] - Object to exclude some methods from hoisting, third parameter of hoist-non-react-statics module. | ||
* By default excludes forwardRef component properties in case we wrap another forwardRef component. | ||
* Until https://github.com/mridgway/hoist-non-react-statics/issues/48 is resolved | ||
* @returns {Object} | ||
*/ | ||
export const forwardRefFactory = ( | ||
Component, | ||
{displayName, hoistSource = Component, hoistExclude = {$$typeof: true, render: true}} = {} | ||
) => { | ||
const forwardFn = (props, ref) => <Component {...{[forwardRefSymbol]: ref, ...props}}/>; | ||
|
||
forwardFn.displayName = displayName || Component.displayName || Component.name; | ||
|
||
const forwarder = forwardRef(forwardFn); | ||
|
||
hoistStatics(forwarder, hoistSource, hoistExclude); | ||
|
||
return forwarder; | ||
}; | ||
|
||
/** | ||
* Simple HOC that uses forwardRefFactory | ||
* @param [options] - Options for forwardRefFactory | ||
* @returns {Object} | ||
*/ | ||
export function withForwardRef(options) { | ||
return Component => forwardRefFactory(Component, options); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
const path = require('path'); | ||
|
||
let babelOptions; | ||
let filename = '[name].js'; | ||
const minify = process.env.BUILD_MODE.endsWith('min'); | ||
|
||
if (!process.env.BUILD_MODE.startsWith('umd')) { | ||
// To see es* prefix in webpack stat output, concatenate folder with filename | ||
filename = `${process.env.BUILD_MODE.match(/^([^-]+)/)[1]}/${filename}`; | ||
} | ||
|
||
if (process.env.BUILD_MODE.startsWith('umd')) { | ||
babelOptions = { | ||
presets: [ | ||
'react', | ||
'env', | ||
], | ||
plugins: [ | ||
'transform-class-properties', | ||
'transform-object-rest-spread', | ||
['transform-react-remove-prop-types', {mode: 'remove'}], | ||
], | ||
}; | ||
} else if (process.env.BUILD_MODE.startsWith('es2015')) { | ||
babelOptions = { | ||
plugins: [ | ||
['transform-react-jsx', {useBuiltIns: true}], | ||
'syntax-jsx', | ||
|
||
'transform-async-to-generator', | ||
|
||
'transform-class-properties', | ||
['transform-object-rest-spread', {useBuiltIns: true}], | ||
['transform-react-remove-prop-types', {mode: 'remove'}], | ||
], | ||
}; | ||
} else if (process.env.BUILD_MODE.startsWith('es2017')) { | ||
babelOptions = { | ||
plugins: [ | ||
['transform-react-jsx', {useBuiltIns: true}], | ||
'syntax-jsx', | ||
|
||
'transform-class-properties', | ||
['transform-object-rest-spread', {useBuiltIns: true}], | ||
['transform-react-remove-prop-types', {mode: 'remove'}], | ||
], | ||
}; | ||
} | ||
|
||
module.exports = { | ||
entry: { | ||
[`react-forwardref-utils${minify ? '.min' : ''}`]: './src/index.js', | ||
}, | ||
output: { | ||
filename, | ||
sourceMapFilename: `${filename}.map`, | ||
path: path.resolve(__dirname, 'dist'), | ||
pathinfo: false, | ||
libraryTarget: 'umd', | ||
library: 'SizeWatcher', | ||
}, | ||
devtool: 'source-map', | ||
mode: 'production', | ||
optimization: { | ||
removeAvailableModules: true, | ||
removeEmptyChunks: true, | ||
mergeDuplicateChunks: true, | ||
flagIncludedChunks: true, | ||
occurrenceOrder: true, | ||
providedExports: true, | ||
usedExports: true, | ||
sideEffects: true, | ||
concatenateModules: true, | ||
splitChunks: false, | ||
runtimeChunk: false, | ||
noEmitOnErrors: true, | ||
namedModules: true, | ||
namedChunks: true, | ||
nodeEnv: 'production', | ||
minimize: minify, | ||
}, | ||
resolve: { | ||
modules: [ | ||
path.resolve('src'), | ||
'node_modules', | ||
], | ||
}, | ||
externals: { | ||
'react': 'umd react', | ||
'hoist-non-react-statics': { | ||
amd: 'hoist-non-react-statics', | ||
root: 'hoistNonReactStatics', | ||
commonjs: 'hoist-non-react-statics', | ||
commonjs2: 'hoist-non-react-statics', | ||
}, | ||
}, | ||
module: { | ||
rules: [ | ||
{ | ||
test: /\.js$/, | ||
exclude: /node_modules/, | ||
use: { | ||
loader: 'babel-loader', | ||
options: babelOptions, | ||
}, | ||
}, | ||
], | ||
}, | ||
node: { | ||
process: false, | ||
setImmediate: false, | ||
}, | ||
stats: { | ||
assets: true, | ||
colors: true, | ||
errors: true, | ||
errorDetails: true, | ||
hash: false, | ||
timings: true, | ||
version: true, | ||
warnings: true, | ||
entrypoints: false, | ||
modules: false, | ||
}, | ||
}; |