Skip to content

Commit 2451875

Browse files
layershifterAlexander Fedyashov
authored and
Alexander Fedyashov
committed
perf(props): Remove propTypes from production build
perf(props): Remove propTypes from production build
1 parent ec2081a commit 2451875

File tree

5 files changed

+99
-63
lines changed

5 files changed

+99
-63
lines changed

.github/CONTRIBUTING.md

+23-4
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class Dropdown extends Component {
126126
127127
### Define _meta
128128

129-
Every component has a static property called `_meta`. This object defines the component. The values here are used in `propTypes`, generated documentation, generated test cases, and some utilities.
129+
Every component has a static property called `_meta`. This object defines the component. The values here are used for handling props, generated documentation, generated test cases and some utilities.
130130

131131
Here's an example `_meta` object:
132132

@@ -136,9 +136,7 @@ import { META } from '../../lib'
136136
const _meta = {
137137
name: 'MyComponent',
138138
type: META.TYPES.MODULE,
139-
props: {
140-
pointing: ['bottom left', 'bottom right'],
141-
},
139+
props: ['as', 'children', 'className'],
142140
}
143141
```
144142

@@ -162,6 +160,27 @@ class MyComponent {
162160
}
163161
```
164162

163+
### Using propTypes
164+
165+
Every component must have fully described `propTypes`, values for them are defined in `props`.
166+
167+
```js
168+
import React, { PropTypes } from 'react'
169+
170+
function MyComponent(props) {
171+
return <div className={props.position}>{props.children}</div>
172+
}
173+
174+
MyComponent.props = {
175+
position: ['left', 'right'],
176+
}
177+
178+
MyComponent.propTypes = {
179+
children: PropTypes.node,
180+
position: PropTypes.oneOf(MyComponent.props),
181+
}
182+
```
183+
165184
### Conformance Test
166185

167186
Review [common tests](#common-tests) below. You should now add the [`isConformant()`](#isconformant-required) common test and get it to pass. This will validate the `_meta` and help you get your component off the ground.

src/elements/Rail/Rail.js

+39-27
Original file line numberDiff line numberDiff line change
@@ -47,43 +47,55 @@ function Rail(props) {
4747
Rail._meta = {
4848
name: 'Rail',
4949
type: META.TYPES.ELEMENT,
50-
props: {
51-
close: ['very'],
52-
position: SUI.FLOATS,
53-
size: _.without(SUI.SIZES, 'medium'),
54-
},
50+
props: [
51+
'as', 'attached', 'children', 'className', 'close', 'dividing', 'internal', 'position', 'size',
52+
],
5553
}
5654

57-
Rail.propTypes = {
58-
/** An element type to render as (string or function). */
59-
as: customPropTypes.as,
55+
if (process.env.NODE_ENV !== 'production') {
56+
Rail.props = {
57+
close: {
58+
values: ['very'],
59+
},
60+
position: {
61+
values: SUI.FLOATS,
62+
},
63+
size: {
64+
values: _.without(SUI.SIZES, 'medium'),
65+
},
66+
}
6067

61-
/** A rail can appear attached to the main viewport. */
62-
attached: PropTypes.bool,
68+
Rail.propTypes = {
69+
/** An element type to render as (string or function). */
70+
as: customPropTypes.as,
6371

64-
/** Primary content. */
65-
children: PropTypes.node,
72+
/** A rail can appear attached to the main viewport. */
73+
attached: PropTypes.bool,
6674

67-
/** Additional classes. */
68-
className: PropTypes.string,
75+
/** Primary content. */
76+
children: PropTypes.node,
6977

70-
/** A rail can appear closer to the main viewport. */
71-
close: PropTypes.oneOfType([
72-
PropTypes.bool,
73-
PropTypes.oneOf(Rail._meta.props.close),
74-
]),
78+
/** Additional classes. */
79+
className: PropTypes.string,
7580

76-
/** A rail can create a division between itself and a container. */
77-
dividing: PropTypes.bool,
81+
/** A rail can appear closer to the main viewport. */
82+
close: PropTypes.oneOfType([
83+
PropTypes.bool,
84+
PropTypes.oneOf(Rail.props.close),
85+
]),
7886

79-
/** A rail can attach itself to the inside of a container. */
80-
internal: PropTypes.bool,
87+
/** A rail can create a division between itself and a container. */
88+
dividing: PropTypes.bool,
8189

82-
/** A rail can be presented on the left or right side of a container. */
83-
position: PropTypes.oneOf(Rail._meta.props.position).isRequired,
90+
/** A rail can attach itself to the inside of a container. */
91+
internal: PropTypes.bool,
8492

85-
/** A rail can have different sizes. */
86-
size: PropTypes.oneOf(Rail._meta.props.size),
93+
/** A rail can be presented on the left or right side of a container. */
94+
position: PropTypes.oneOf(Rail.props.position).isRequired,
95+
96+
/** A rail can have different sizes. */
97+
size: PropTypes.oneOf(Rail.props.size),
98+
}
8799
}
88100

89101
export default Rail

src/lib/getUnhandledProps.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import _ from 'lodash'
2+
23
/**
34
* Returns an object consisting of props beyond the scope of the Component.
45
* Useful for getting and spreading unknown props from the user.
@@ -7,11 +8,7 @@ import _ from 'lodash'
78
* @returns {{}} A shallow copy of the prop object
89
*/
910
const getUnhandledProps = (Component, props) => {
10-
const handledProps = _.union(
11-
Component.autoControlledProps,
12-
_.keys(Component.defaultProps),
13-
_.keys(Component.propTypes),
14-
)
11+
const handledProps = Component._meta ? Component._meta.props || [] : []
1512

1613
return _.omit(props, handledProps)
1714
}

test/specs/commonTests.js

+26-5
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,27 @@ export const isConformant = (Component, options = {}) => {
254254
})
255255
})
256256

257+
describe('handles props', () => {
258+
it('defines handled props in Component._meta.props', () => {
259+
Component.should.have.any.keys('_meta')
260+
Component._meta.should.have.any.keys('props')
261+
Component._meta.props.should.be.an('array')
262+
})
263+
264+
it('Component._meta.props includes all handled props', () => {
265+
const computedProps = _.union(
266+
Component.autoControlledProps,
267+
_.keys(Component.defaultProps),
268+
_.keys(Component.propTypes),
269+
)
270+
271+
Component._meta.props.should.to.deep.equal(computedProps,
272+
'It seems that not all props were defined in Component._meta.props, you need to check that they equal to ' +
273+
'union of Component.autoControlledProps and keys of Component.defaultProps and Component.propTypes'
274+
)
275+
})
276+
})
277+
257278
// ----------------------------------------
258279
// Events
259280
// ----------------------------------------
@@ -466,11 +487,11 @@ export const rendersChildren = (Component, options = {}) => {
466487
// className from prop
467488
// ----------------------------------------
468489
const _definesPropOptions = (Component, propKey) => {
469-
it(`defines ${propKey} options in Component._meta.props`, () => {
470-
Component.should.have.any.keys('_meta')
471-
Component._meta.should.have.any.keys('props')
472-
Component._meta.props.should.have.any.keys(propKey)
473-
Component._meta.props[propKey].should.be.an('array')
490+
it(`defines ${propKey} options in Component.props`, () => {
491+
Component.should.have.any.keys('props')
492+
Component.props.should.have.any.keys(propKey)
493+
Component.props[propKey].should.have.any.keys('values')
494+
Component.props[propKey].values.should.be.an('array')
474495
})
475496
}
476497

test/specs/lib/getUnhandledProps-test.js

+9-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import React, { PropTypes } from 'react'
2-
1+
import React from 'react'
32
import { getUnhandledProps } from 'src/lib'
43

54
// We spread the unhandled props onto the rendered result.
@@ -10,33 +9,21 @@ function TestComponent(props) {
109
}
1110

1211
beforeEach(() => {
13-
delete TestComponent.propTypes
14-
delete TestComponent.defaultProps
15-
delete TestComponent.autoControlledProps
12+
delete TestComponent._meta
1613
})
1714

1815
describe('getUnhandledProps', () => {
19-
it('removes props defined in propTypes', () => {
20-
TestComponent.propTypes = { 'data-remove-me': PropTypes.string }
16+
it('removes props defined in _meta.props', () => {
17+
TestComponent._meta = { props: ['data-remove-me'] }
2118
shallow(<TestComponent />)
2219
.should.not.have.prop('data-remove-me', 'thanks')
2320
})
24-
it('removes props defined in defaultProps', () => {
25-
TestComponent.defaultProps = { 'data-remove-me': 'thanks' }
26-
shallow(<TestComponent />)
27-
.should.not.have.prop('data-remove-me', 'thanks')
28-
})
29-
it('removes props defined in autoControlledProps', () => {
30-
TestComponent.autoControlledProps = ['data-remove-me']
31-
shallow(<TestComponent />)
32-
.should.not.have.prop('data-remove-me')
33-
})
34-
it('removes default versions of autoControlledProps', () => {
35-
TestComponent.autoControlledProps = ['data-remove-me']
36-
shallow(<TestComponent />)
37-
.should.not.have.prop('defaultRemoveMe')
21+
it('leaves props that are not defined _meta.props', () => {
22+
TestComponent._meta = {}
23+
shallow(<TestComponent data-leave-this='it is unhandled' />)
24+
.should.have.prop('data-leave-this')
3825
})
39-
it('leaves props that are not defined in propTypes', () => {
26+
it('leaves props that are not defined _meta.props', () => {
4027
shallow(<TestComponent data-leave-this='it is unhandled' />)
4128
.should.have.prop('data-leave-this')
4229
})

0 commit comments

Comments
 (0)