|
| 1 | +## Legacy `react-codemod` |
| 2 | + |
| 3 | +All codemods in this repo can be run using the legacy `react-codemod` command, which runs the codemods via jscodeshift directly. |
| 4 | + |
| 5 | +### Usage |
| 6 | + |
| 7 | +We recommend using the [`codemod`](github.com/reactjs/react-codemod) command for improved experience and support. |
| 8 | + |
| 9 | +To use `react-codemod` directly: |
| 10 | + |
| 11 | +`npx react-codemod <transform> <path> [...options]` |
| 12 | +* `transform` - name of transform, see available transforms below. |
| 13 | +* `path` - files or directory to transform |
| 14 | +* use the `--dry` option for a dry-run and use `--print` to print the output for comparison |
| 15 | + |
| 16 | +This will start an interactive wizard, and then run the specified transform. |
| 17 | + |
| 18 | +### Included Transforms |
| 19 | + |
| 20 | +#### `remove-context-provider` |
| 21 | + |
| 22 | +Converts `Context.Provider` JSX opening and closing elements into `Context`. |
| 23 | + |
| 24 | +```sh |
| 25 | +npx react-codemod remove-context-provider <path> |
| 26 | +``` |
| 27 | + |
| 28 | +#### `remove-forward-ref` |
| 29 | + |
| 30 | +Removes usages of `forwardRef`. |
| 31 | + |
| 32 | +```sh |
| 33 | +npx react-codemod remove-forward-ref <path> |
| 34 | +``` |
| 35 | + |
| 36 | +#### `use-context-hook` |
| 37 | + |
| 38 | +```sh |
| 39 | +npx react-codemod use-context-hook <path> |
| 40 | +``` |
| 41 | + |
| 42 | +#### `replace-act-import` |
| 43 | + |
| 44 | +```sh |
| 45 | +npx react-codemod replace-act-import <path> |
| 46 | +``` |
| 47 | + |
| 48 | +#### `replace-string-ref` |
| 49 | + |
| 50 | +Replaces deprecated string refs with callback refs. |
| 51 | + |
| 52 | +```sh |
| 53 | +npx react-codemod replace-string-ref <path> |
| 54 | +``` |
| 55 | + |
| 56 | +#### `replace-use-form-state` |
| 57 | + |
| 58 | +Replaces usages of useFormState() to use useActionState(). |
| 59 | + |
| 60 | +```sh |
| 61 | +npx react-codemod replace-use-form-state <path> |
| 62 | +``` |
| 63 | + |
| 64 | +#### `replace-reactdom-render` |
| 65 | + |
| 66 | +Replaces usages of ReactDom.render() with createRoot(node).render(). |
| 67 | + |
| 68 | +```sh |
| 69 | +npx react-codemod replace-reactdom-render <path> |
| 70 | +``` |
| 71 | + |
| 72 | +#### `create-element-to-jsx` |
| 73 | + |
| 74 | +Converts calls to `React.createElement` into JSX elements. |
| 75 | + |
| 76 | +```sh |
| 77 | +npx react-codemod create-element-to-jsx <path> |
| 78 | +``` |
| 79 | + |
| 80 | +#### `error-boundaries` |
| 81 | + |
| 82 | +Renames the experimental `unstable_handleError` lifecycle hook to `componentDidCatch`. |
| 83 | + |
| 84 | +```sh |
| 85 | +npx react-codemod error-boundaries <path> |
| 86 | +``` |
| 87 | + |
| 88 | +#### `findDOMNode` |
| 89 | + |
| 90 | +Updates `this.getDOMNode()` or `this.refs.foo.getDOMNode()` calls inside of |
| 91 | +`React.createClass` components to `React.findDOMNode(foo)`. Note that it will |
| 92 | +only look at code inside of `React.createClass` calls and only update calls on |
| 93 | +the component instance or its refs. You can use this script to update most calls |
| 94 | +to `getDOMNode` and then manually go through the remaining calls. |
| 95 | + |
| 96 | +```sh |
| 97 | +npx react-codemod findDOMNode <path> |
| 98 | +``` |
| 99 | + |
| 100 | +#### `manual-bind-to-arrow` |
| 101 | + |
| 102 | +Converts manual function bindings in a class (e.g., `this.f = this.f.bind(this)`) to arrow property initializer functions (e.g., `f = () => {}`). |
| 103 | + |
| 104 | +```sh |
| 105 | +npx react-codemod manual-bind-to-arrow <path> |
| 106 | +``` |
| 107 | + |
| 108 | +#### `pure-component` |
| 109 | + |
| 110 | +Converts ES6 classes that only have a render method, only have safe properties |
| 111 | +(statics and props), and do not have refs to Functional Components. |
| 112 | + |
| 113 | +The wizard will ask for 2 options - |
| 114 | + |
| 115 | +* **Use arrow functions?**: converts to arrow function. Converts to `function` by default. |
| 116 | +* **Destructure props?**: will destructure props in the argument where it is safe to do so. |
| 117 | + |
| 118 | +```sh |
| 119 | +npx react-codemod pure-component <path> |
| 120 | +``` |
| 121 | + |
| 122 | +#### `pure-render-mixin` |
| 123 | + |
| 124 | +Removes `PureRenderMixin` and inlines `shouldComponentUpdate` so that the ES2015 |
| 125 | +class transform can pick up the React component and turn it into an ES2015 |
| 126 | +class. NOTE: This currently only works if you are using the master version |
| 127 | +(>0.13.1) of React as it is using `React.addons.shallowCompare` |
| 128 | + |
| 129 | +```sh |
| 130 | +npx react-codemod pure-render-mixin <path> |
| 131 | +``` |
| 132 | + |
| 133 | +* The wizard will ask to optionally override `mixin-name`, and look for it |
| 134 | + instead of `PureRenderMixin`. Note that it is not possible to use a |
| 135 | + namespaced name for the mixin. `mixins: [React.addons.PureRenderMixin]` will |
| 136 | + not currently work. |
| 137 | + |
| 138 | +#### `React-PropTypes-to-prop-types` |
| 139 | + |
| 140 | +Replaces `React.PropTypes` references with `prop-types` and adds the appropriate `import` or `require` statement. This codemod is intended for React 15.5+. |
| 141 | + |
| 142 | +```sh |
| 143 | +npx react-codemod React-PropTypes-to-prop-types <path> |
| 144 | +``` |
| 145 | + |
| 146 | +* In addition to running the above codemod you will also need to install the `prop-types` NPM package. |
| 147 | + |
| 148 | +#### `rename-unsafe-lifecycles` |
| 149 | + |
| 150 | +Adds `UNSAFE_` prefix for deprecated lifecycle hooks. (For more information about this codemod, see [React RFC #6](https://github.com/reactjs/rfcs/pull/6)) |
| 151 | + |
| 152 | +```sh |
| 153 | +npx react-codemod rename-unsafe-lifecycles <path> |
| 154 | +``` |
| 155 | + |
| 156 | +#### `react-to-react-dom` |
| 157 | + |
| 158 | +Updates code for the split of the `react` and `react-dom` packages (e.g., |
| 159 | +`React.render` to `ReactDOM.render`). It looks for `require('react')` and |
| 160 | +replaces the appropriate property accesses using `require('react-dom')`. It does |
| 161 | +not support ES6 modules or other non-CommonJS systems. We recommend performing |
| 162 | +the `findDOMNode` conversion first. |
| 163 | + |
| 164 | + |
| 165 | +```sh |
| 166 | +npx react-codemod react-to-react-dom <path> |
| 167 | +``` |
| 168 | + |
| 169 | +* After running the automated codemod, you may want to run a regex-based |
| 170 | + find-and-replace to remove extra whitespace between the added requires, such |
| 171 | + as `codemod.py -m -d src --extensions js '(var |
| 172 | + React\s*=\s*require\(.react.\);)\n\n(\s*var ReactDOM)' '\1\n\2'` using |
| 173 | + https://github.com/facebook/codemod. |
| 174 | + |
| 175 | +#### `React-DOM-to-react-dom-factories` |
| 176 | + |
| 177 | +Converts calls like `React.DOM.div(...)` to `React.createElement('div', ...)`. |
| 178 | + |
| 179 | +```sh |
| 180 | +npx react-codemod React-DOM-to-react-dom-factories <path> |
| 181 | +``` |
| 182 | + |
| 183 | +#### `ReactNative-View-propTypes` |
| 184 | + |
| 185 | +Replaces `View.propTypes` references with `ViewPropTypes` and adds the appropriate `import` or `require` statement. This codemod is intended for ReactNative 44+. |
| 186 | + |
| 187 | +```sh |
| 188 | +npx react-codemod ReactNative-View-propTypes <path> |
| 189 | +``` |
| 190 | + |
| 191 | +#### `sort-comp` |
| 192 | + |
| 193 | +Reorders React component methods to match the [ESLint](http://eslint.org/) |
| 194 | +[react/sort-comp |
| 195 | +rule](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md). (Defaults to ordering of the [Airbnb style |
| 196 | +guide](https://github.com/airbnb/javascript/blob/7684892951ef663e1c4e62ad57d662e9b2748b9e/packages/eslint-config-airbnb/rules/react.js#L122-L134). |
| 197 | + |
| 198 | +`react-codemod`: |
| 199 | +```sh |
| 200 | +npx react-codemod sort-comp <path> |
| 201 | +``` |
| 202 | + |
| 203 | +#### `update-react-imports` |
| 204 | + |
| 205 | +[As of Babel 7.9.0](https://babeljs.io/blog/2020/03/16/7.9.0#a-new-jsx-transform-11154-https-githubcom-babel-babel-pull-11154), when using `runtime: automatic` in `@babel/preset-react` or `@babel/plugin-transform-react-jsx`, you will not need to explicitly import React for compiling jsx. This codemod removes the redundant import statements. It also converts default imports (`import React from 'react'`) to named imports (e.g. `import { useState } from 'react'`). |
| 206 | + |
| 207 | +The wizard will ask for 1 option - |
| 208 | + |
| 209 | +* **Destructure namespace imports as well?**: If chosen, *namespace* imports like `import * as React` will *also* be converted. By default, it's false, so only default imports (`import React`) are converted. |
| 210 | + |
| 211 | +```sh |
| 212 | +npx react-codemod update-react-imports <path> |
| 213 | +``` |
| 214 | + |
| 215 | +### Explanation of the new ES2015 class transform with property initializers |
| 216 | +1. Determine if mixins are convertible. We only transform a `createClass` call to an ES6 class component when: |
| 217 | +- There are no mixins on the class, or |
| 218 | +- `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`) |
| 219 | +2. Ignore components that: |
| 220 | +- 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 |
| 221 | +- Explicitly call `this.getInitialState()` and/or `this.getDefaultProps()` since an ES6 class component will no longer have these methods |
| 222 | +- 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 |
| 223 | +- 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 |
| 224 | +- Have non-primitive right hand side values (like `foo: getStuff()`) in the class spec |
| 225 | +3. Transform it to an ES6 class component |
| 226 | +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 |
| 227 | + - Remove the `require`/`import` statement that imports pure render mixin when it's no longer being referenced |
| 228 | +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 = {...};`) |
| 229 | + - If `getDefaultProps()` is simple (i.e. it only contains a return statement that returns something) 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 |
| 230 | +3. Transform `getInitialState()` |
| 231 | + - If there's no `getInitialState()` or the `getInitialState()` function is simple (i.e., it only contains a return statement that returns something) then we don't need a constructor; `state` will be lifted to a property initializer (`state = ...;`) |
| 232 | + - However, if the RHS of `return` 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 |
| 233 | + - If `getInitialState()` is not simple, we create a `constructor` and convert `getInitialState()` to an assignment to `this.state` |
| 234 | + - `constructor` always have `props` as the first parameter |
| 235 | + - We only put `context` as the second parameter when (one of) the following things happen in `getInitialState()`: |
| 236 | + - It accesses `this.context`, or |
| 237 | + - There's a direct method call `this.x()`, or |
| 238 | + - `this` is referenced alone |
| 239 | + - Rewrite accesses to `this.props` to `props` and accesses to `this.context` to `context` since the values will be passed as `constructor` arguments |
| 240 | + - Remove _simple_ variable declarations like `var props = this.props;` and `var context = this.context` |
| 241 | + - Rewrite top-level return statements (`return {...};`) to `this.state = {...}` |
| 242 | + - 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 |
| 243 | +4. Transform all non-lifecycle methods and fields to class property initializers (like `onClick = () => {};`). All your Flow annotations will be preserved |
| 244 | + - 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 |
| 245 | +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`) |
| 246 | +- 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` |
| 247 | +- Notice that Flow treats an optional propType as non-nullable |
| 248 | + - For example, `foo: React.PropTypes.number` is valid when you pass `{}`, `{foo: null}`, or `{foo: undefined}` as props at **runtime**. However, when Flow infers type from a `createClass` call, only `{}` and `{foo: undefined}` are valid; `{foo: null}` is not. Thus the equivalent type annotation in Flow is actually `{foo?: number}`. The question mark on the left hand side indicates `{}` and `{foo: undefined}` are fine, but when `foo` is present it must be a `number` |
| 249 | +- For `propTypes` fields that can't be recognized by Flow, `$FlowFixMe` will be used |
| 250 | +5. `React.createClass` is no longer present in React 16. So, if a `createClass` call cannot be converted to a plain class, the script will fallback to using the `create-react-class` package. |
| 251 | +- Replaces `React.createClass` with `ReactCreateClass`. |
| 252 | +- Adds a `require` or `import` statement for `create-react-class`. The import style is inferred from the import style of the `react` import. The default module name can be overridden with the `--create-class-module-name` option. |
| 253 | +- Prunes the `react` import if there are no more references to it. |
| 254 | + |
| 255 | + |
| 256 | +## `react-codemod` options |
| 257 | + |
| 258 | +### jscodeshift options |
| 259 | + |
| 260 | +To pass more options directly to jscodeshift, use `--jscodeshift="..."`. For example: |
| 261 | +```sh |
| 262 | +npx react-codemod --jscodeshift="--run-in-band --verbose=2" |
| 263 | +``` |
| 264 | + |
| 265 | +See all available options [here](https://github.com/facebook/jscodeshift#usage-cli). |
| 266 | + |
| 267 | +### Recast Options |
| 268 | + |
| 269 | +Options to [recast](https://github.com/benjamn/recast)'s printer can be provided |
| 270 | +through jscodeshift's `printOptions` command line argument |
| 271 | + |
| 272 | +```sh |
| 273 | +npx react-codemod <transform> <path> --jscodeshift="--printOptions='{\"quote\":\"double\"}'" |
| 274 | +``` |
| 275 | + |
| 276 | +### `explicit-require=false` |
| 277 | + |
| 278 | +If you're not explicitly importing React in your files (eg: if you're loading React with a script tag), you should add `--explicit-require=false`. |
| 279 | + |
0 commit comments