Skip to content

Commit 8b15535

Browse files
author
Keyan Zhang
committed
updated README.md
1 parent 667e592 commit 8b15535

File tree

1 file changed

+34
-29
lines changed

1 file changed

+34
-29
lines changed

README.md

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -102,35 +102,40 @@ jscodeshift -t react-codemod/transforms/sort-comp.js <path>
102102
```
103103

104104
### Explanation of the new ES2015 class transform with property initializers
105-
106-
* Ignore components with calls to deprecated APIs. This is very defensive, if
107-
the script finds any identifiers called `isMounted`, `getDOMNode`,
108-
`replaceProps`, `replaceState` or `setProps` it will skip the component.
109-
* Replaces `var A = React.createClass(spec)` with
110-
`class A (extends React.Component) {spec}`.
111-
* Pulls out all statics defined on `statics` plus the few special cased
112-
statics like `propTypes`, `childContextTypes`, `contextTypes`, and
113-
`displayName` and transforms them to `static` properties at the very top.
114-
* Takes `getDefaultProps` and inlines it as `static defaultProps = ...;`.
115-
If `getDefaultProps` is defined as a function with a single statement that
116-
returns an object, it optimizes and transforms
117-
`getDefaultProps() { return {foo: 'bar'}; }` into
118-
`static defaultProps = {foo: 'bar'};`. If `getDefaultProps` contains more than
119-
one statement it will transform into a self-invoking function like this:
120-
`static defaultProps = (function() {…})();`. Note that this means that the function
121-
will be executed only a single time per app-lifetime. In practice this
122-
hasn't caused any issues – `getDefaultProps` should not contain any
123-
side-effects.
124-
* If there exists references to `this.props` in `getInitialState` then it creates
125-
a constructor and converts `getInitialState` to an assignment to `this.state`;
126-
Otherwise it lifts `getInitialState` to a property initializer (`state = ...;`).
127-
* Transforms class methods to arrow functions as class property initializers
128-
(i.e., to bind them) if methods are referenced without being
129-
called directly. It checks for `this.foo` but also traces variable
130-
assignments like `var self = this; self.foo`. It does not bind functions
131-
from the React API (lifecycle methods) and ignores functions that are being
132-
called directly (unless it is both called directly and passed around to
133-
somewhere else).
105+
1. Determine if mixins are convertible. We only transform a `createClass` call to an ES6 class component when:
106+
- There are no mixins on the class, or
107+
- `options['pure-component']` is true, the `mixins` property is an array and it _only_ contains pure render mixin (the specific module name can be specified using `options['mixin-module-name']`, which defaults to `react-addons-pure-render-mixin`)
108+
2. Ignore components that:
109+
- Call deprecated APIs. This is very defensive, if the script finds any identifiers called `isMounted`, `getDOMNode`, `replaceProps`, `replaceState` or `setProps` it will skip the component
110+
- Explicitly call `this.getInitialState()` and/or `this.getDefaultProps()` since an ES6 class component will no longer have these methods
111+
- Use `arguments` in methods since arrow functions don't have `arguments`. Also please notice that `arguments` should be [very carefully used](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments) and it's generally better to switch to spread (`...args`) instead
112+
- Have inconvertible `getInitialState()`. Specifically if you have variable declarations like `var props = ...` and the right hand side is not `this.props` then we can't inline the state initialization in the `constructor` due to variable shadowing issues
113+
- Have non-primitive right hand side values (like `foo: getStuff()`) in the class spec
114+
3. Transform it to an ES6 class component
115+
1. Replace `var A = React.createClass(spec)` with `class A extends React.Component {spec}`. If a component uses pure render mixin and passes the mixins test (as described above), it will extend `React.PureComponent` instead
116+
- Remove the `require`/`import` statement that imports pure render mixin when it's no longer being referenced
117+
2. Pull out all statics defined on `statics` plus the few special cased statics like `childContextTypes`, `contextTypes`, `displayName`, `getDefaultProps()`, and `propTypes` and transform them to `static` properties (`static propTypes = {...};`)
118+
- If `getDefaultProps()` is simple (i.e. it only contains a return statement that returns an object) it will be converted to a simple assignment (`static defaultProps = {...};`). Otherwise an IIFE (immediately-invoked function expression) will be created (`static defaultProps = function() { ... }();`). Note that this means that the function will be executed only a single time per app-lifetime. In practice this hasn't caused any issues — `getDefaultProps` should not contain any side-effects
119+
3. Transform `getInitialState()`
120+
- If there's no `getInitialState()` or the `getInitialState()` function is simple (i.e., it only contains a return statement that returns an object) then we don't need a constructor; `state` will be lifted to a property initializer (`state = {...};`)
121+
- However, if the object contains references to `this` other than `this.props` and/or `this.context`, we can't be sure about what you'll need from `this`. We need to ensure that our property initializers' evaluation order is safe, so we defer `state`'s initialization by moving it all the way down until all other property initializers have been initialized
122+
- If `getInitialState()` is not simple, we create a `constructor` and convert `getInitialState()` to an assignment to `this.state`
123+
- `constructor` always have `props` as the first parameter
124+
- We only put `context` as the second parameter when (one of) the following things happen in `getInitialState()`:
125+
- It accesses `this.context`, or
126+
- There's a direct method call `this.x()`, or
127+
- `this` is referenced alone
128+
- Rewrite accesses to `this.props` to `props` and accesses to `this.context` to `context` since the values will be passed as `constructor` arguments
129+
- Remove _simple_ variable declarations like `var props = this.props;` and `var context = this.context`
130+
- Rewrite top-level return statements (`return {...};`) to `this.state = {...}`
131+
- Add `return;` after the assignment when the return statement is part of a control flow statement (not a direct child of `getInitialState()`'s body) and not in an inner function declaration
132+
4. Transform all non-lifecycle methods and fields to class property initializers (like `onClick = () => {};`). All your Flow annotations will be preserved
133+
- It's actually not necessary to transform all methods to arrow functions (i.e., to bind them), but this behavior is the same as `createClass()` and we can make sure that we won't accidentally break stuff
134+
4. Generate Flow annotations from `propTypes` and put it on the class (this only happens when there's `/* @flow */` in your code and `options['flow']` is `true`)
135+
- Flow actually understands `propTypes` in `createClass` calls but not ES6 class components. Here the transformation logic is identical to [how](https://github.com/facebook/flow/blob/master/src/typing/statement.ml#L3526) Flow treats `propTypes`
136+
- Notice that `React.PropTypes` and Flow treat optional values differently
137+
- For example, `foo: React.PropTypes.number` is valid when you pass `{}`, `{foo: null}`, or `{foo: undefined}` as props. The equivalent in Flow is actually `foo?: ?number`; the question mark on the left hand side indicates `{}` is valid
138+
- For `propTypes` fields that can't be recognized by Flow, `any` will be used
134139

135140
### Recast Options
136141

0 commit comments

Comments
 (0)