Skip to content
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

Merged
merged 1 commit into from
May 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/faq/BundleSize.md
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.
Copy link
Member

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. 😛

Copy link
Contributor Author

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?

Copy link
Contributor Author

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. 😃

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #2898

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)
Copy link

Choose a reason for hiding this comment

The 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 babel-plugin-transform-imports or do I still need to use react-router/es/actions when using the plugin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try to be more explicit about the problem​ then.

Copy link
Member

Choose a reason for hiding this comment

The 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
```
2 changes: 2 additions & 0 deletions docs/faq/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ Below is a list of common problems or questions that people have using `redux-fo
8. [Can I submit my form using websockets?](WebsocketSubmit.md)

9. [How do I `mapStateToProps` or `mapDispatchToProps`?](HowToConnect.md)

9. [How to reduce `redux-form`'s bundle size?](BundleSize.md)
128 changes: 3 additions & 125 deletions src/Field.js
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)
105 changes: 3 additions & 102 deletions src/FieldArray.js
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)