-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow direct imports to reduce bundle size. #2893
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# How to reduce `redux-form`'s bundle size? | ||
|
||
For convenience, Redux Form exposes its full API on the top-level `redux-form` import. | ||
However, this causes the entire Redux Form library and its dependencies to be included in client bundles that | ||
include code that imports from the top-level import. | ||
|
||
Instead, the bindings exported from `redux-form` are also available in `redux-form/lib` and `redux-form/es`. | ||
You can import directly from those to avoid pulling in unused modules. | ||
|
||
Example: | ||
```js | ||
import { reduxForm, Field, FieldArray } from 'redux-form' | ||
``` | ||
|
||
use: | ||
|
||
```js | ||
import reduxForm from 'react-router/es/reduxForm' | ||
import Field from 'react-router/es/Field' | ||
import FieldArray from 'react-router/es/FieldArray' | ||
``` | ||
|
||
|
||
Use `es` if you are using a bundler that can process ES modules like `webpack@2` or `rollup`, otherwise use `lib`. | ||
|
||
The public API available in this manner is defined as the set of imports available from the top-level `react-router` module. | ||
Anything not available through the top-level `redux-form` module is a private API, and is subject to change without notice. | ||
|
||
## Babel Plugin | ||
|
||
Thankfully there is a babel plugin that can automate this task. | ||
|
||
|
||
[babel-plugin-transform-imports](https://www.npmjs.com/package/babel-plugin-transform-imports) | ||
|
||
|
||
```json | ||
{ | ||
"plugins": [ | ||
["transform-imports", { | ||
"redux-form": { | ||
"transform": "redux-form/es/${member}", | ||
"preventFullImport": true | ||
} | ||
}] | ||
] | ||
} | ||
``` | ||
|
||
##Caveat (Action Creators) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the documentation, it is not clear whether this step is automated by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will try to be more explicit about the problem then. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LOL, even the follow-up question didn't notice the wrong library name. 😆 |
||
|
||
Action creators are available under `actions` in order to take advantage of this method. | ||
One would import the actions binding and then extract the needed action creators. | ||
|
||
Example: | ||
|
||
``` | ||
import actions from 'react-router/es/actions' | ||
|
||
const {change, destroy} = actions | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,126 +1,4 @@ | ||
import { Component, createElement } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import invariant from 'invariant' | ||
import createConnectedField from './ConnectedField' | ||
import shallowCompare from './util/shallowCompare' | ||
import prefixName from './util/prefixName' | ||
import createField from './createField' | ||
import plain from './structure/plain' | ||
|
||
|
||
const createField = ({ deepEqual, getIn, setIn, toJS }) => { | ||
|
||
const ConnectedField = createConnectedField({ | ||
deepEqual, | ||
getIn, | ||
toJS | ||
}) | ||
|
||
class Field extends Component { | ||
constructor(props, context) { | ||
super(props, context) | ||
if (!context._reduxForm) { | ||
throw new Error('Field must be inside a component decorated with reduxForm()') | ||
} | ||
|
||
this.normalize = this.normalize.bind(this) | ||
} | ||
|
||
shouldComponentUpdate(nextProps, nextState) { | ||
return shallowCompare(this, nextProps, nextState) | ||
} | ||
|
||
componentWillMount() { | ||
this.context._reduxForm.register( | ||
this.name, | ||
'Field', | ||
() => this.props.validate, | ||
() => this.props.warn | ||
) | ||
} | ||
|
||
componentWillReceiveProps(nextProps) { | ||
if (this.props.name !== nextProps.name) { | ||
// unregister old name | ||
this.context._reduxForm.unregister(this.name) | ||
// register new name | ||
this.context._reduxForm.register(prefixName(this.context, nextProps.name), 'Field') | ||
} | ||
} | ||
|
||
componentWillUnmount() { | ||
this.context._reduxForm.unregister(this.name) | ||
} | ||
|
||
getRenderedComponent() { | ||
invariant(this.props.withRef, | ||
'If you want to access getRenderedComponent(), ' + | ||
'you must specify a withRef prop to Field') | ||
return this.refs.connected.getWrappedInstance().getRenderedComponent() | ||
} | ||
|
||
get name() { | ||
return prefixName(this.context, this.props.name) | ||
} | ||
|
||
get dirty() { | ||
return !this.pristine | ||
} | ||
|
||
get pristine() { | ||
return this.refs.connected.getWrappedInstance().isPristine() | ||
} | ||
|
||
get value() { | ||
return this.refs.connected && this.refs.connected.getWrappedInstance().getValue() | ||
} | ||
|
||
normalize(name, value) { | ||
const { normalize } = this.props | ||
if (!normalize) { | ||
return value | ||
} | ||
const previousValues = this.context._reduxForm.getValues() | ||
const previousValue = this.value | ||
const nextValues = setIn(previousValues, name, value) | ||
return normalize( | ||
value, | ||
previousValue, | ||
nextValues, | ||
previousValues | ||
) | ||
} | ||
|
||
render() { | ||
return createElement(ConnectedField, { | ||
...this.props, | ||
name: this.name, | ||
normalize: this.normalize, | ||
_reduxForm: this.context._reduxForm, | ||
ref: 'connected' | ||
}) | ||
} | ||
} | ||
|
||
Field.propTypes = { | ||
name: PropTypes.string.isRequired, | ||
component: PropTypes.oneOfType([ PropTypes.func, PropTypes.string ]).isRequired, | ||
format: PropTypes.func, | ||
normalize: PropTypes.func, | ||
onBlur: PropTypes.func, | ||
onChange: PropTypes.func, | ||
onFocus: PropTypes.func, | ||
onDragStart: PropTypes.func, | ||
onDrop: PropTypes.func, | ||
parse: PropTypes.func, | ||
props: PropTypes.object, | ||
validate: PropTypes.oneOfType([ PropTypes.func, PropTypes.arrayOf(PropTypes.func) ]), | ||
warn: PropTypes.oneOfType([ PropTypes.func, PropTypes.arrayOf(PropTypes.func) ]), | ||
withRef: PropTypes.bool | ||
} | ||
Field.contextTypes = { | ||
_reduxForm: PropTypes.object | ||
} | ||
|
||
return Field | ||
} | ||
|
||
export default createField | ||
export default createField(plain) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,4 @@ | ||
import { Component, createElement } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import invariant from 'invariant' | ||
import createConnectedFieldArray from './ConnectedFieldArray' | ||
import prefixName from './util/prefixName' | ||
import createFieldArray from './createFieldArray' | ||
import plain from './structure/plain' | ||
|
||
const toArray = value => Array.isArray(value) ? value : [ value ] | ||
|
||
const wrapError = (fn, key) => fn && ((...args) => { | ||
const validators = toArray(fn) | ||
for (let i = 0; i < validators.length; i++) { | ||
const result = validators[i](...args) | ||
if (result) { | ||
return { [key]: result } | ||
} | ||
} | ||
}) | ||
|
||
const createFieldArray = ({ deepEqual, getIn, size }) => { | ||
|
||
const ConnectedFieldArray = createConnectedFieldArray({ deepEqual, getIn, size }) | ||
|
||
class FieldArray extends Component { | ||
constructor(props, context) { | ||
super(props, context) | ||
if (!context._reduxForm) { | ||
throw new Error('FieldArray must be inside a component decorated with reduxForm()') | ||
} | ||
} | ||
|
||
componentWillMount() { | ||
this.context._reduxForm.register( | ||
this.name, | ||
'FieldArray', | ||
() => wrapError(this.props.validate, '_error'), | ||
() => wrapError(this.props.warn, '_warning') | ||
) | ||
} | ||
|
||
componentWillReceiveProps(nextProps) { | ||
if (this.props.name !== nextProps.name) { | ||
// unregister old name | ||
this.context._reduxForm.unregister(this.name) | ||
// register new name | ||
this.context._reduxForm.register(prefixName(this.context, nextProps.name), 'FieldArray') | ||
} | ||
} | ||
|
||
componentWillUnmount() { | ||
this.context._reduxForm.unregister(this.name) | ||
} | ||
|
||
get name() { | ||
return prefixName(this.context, this.props.name) | ||
} | ||
|
||
get dirty() { | ||
return this.refs.connected.getWrappedInstance().dirty | ||
} | ||
|
||
get pristine() { | ||
return this.refs.connected.getWrappedInstance().pristine | ||
} | ||
|
||
get value() { | ||
return this.refs.connected.getWrappedInstance().value | ||
} | ||
|
||
getRenderedComponent() { | ||
invariant(this.props.withRef, | ||
'If you want to access getRenderedComponent(), ' + | ||
'you must specify a withRef prop to FieldArray') | ||
return this.refs.connected.getWrappedInstance().getRenderedComponent() | ||
} | ||
|
||
render() { | ||
return createElement(ConnectedFieldArray, { | ||
...this.props, | ||
name: this.name, | ||
syncError: this.syncError, | ||
syncWarning: this.syncWarning, | ||
_reduxForm: this.context._reduxForm, | ||
ref: 'connected' | ||
}) | ||
} | ||
} | ||
|
||
FieldArray.propTypes = { | ||
name: PropTypes.string.isRequired, | ||
component: PropTypes.func.isRequired, | ||
props: PropTypes.object, | ||
validate: PropTypes.oneOfType([ PropTypes.func, PropTypes.arrayOf(PropTypes.func) ]), | ||
warn: PropTypes.oneOfType([ PropTypes.func, PropTypes.arrayOf(PropTypes.func) ]), | ||
withRef: PropTypes.bool | ||
} | ||
FieldArray.contextTypes = { | ||
_reduxForm: PropTypes.object | ||
} | ||
|
||
return FieldArray | ||
} | ||
|
||
export default createFieldArray | ||
export default createFieldArray(plain) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops. Missed a
react-router
. 😛There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
god dammit. Do you fix it or should I?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Next time I 'inspire' myself on the work of others, do cmd+R. 10 year old me would not have done this kind of mistake. 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #2898