UNPKG

9.39 kBMarkdownView Raw
1> These docs are for the v2 release using Babel 6, if you are still on Babel 5 then you should use
2> v1.1 instead.
3>
4> View the `v1.1` docs [here](https://github.com/gaearon/babel-plugin-react-transform/tree/v1.1.1)
5
6# babel-plugin-react-transform
7
8[![react-transform channel on discord](https://img.shields.io/badge/discord-react--transform%40reactiflux-61DAFB.svg?style=flat-square)](http://www.reactiflux.com)
9
10:rocket: **Now with [Babel 6](https://github.com/babel/babel) support** (thank you [@thejameskyle](https://github.com/thejameskyle)!)
11
12This plugin wraps React components with arbitrary transforms. In other words, **it allows you to instrument React components** in any way—limited only by your imagination.
13
14* [Ecosystem](#ecosystem)
15* [Demo Project](#demo-project)
16* [Installation](#installation)
17* [Writing Transforms](#writing-transforms)
18
19## Ecosystem
20
21For a reference implementation, see [**react-transform-boilerplate**](https://github.com/gaearon/react-transform-boilerplate).
22
23#### Transforms
24
25* [**react-transform-hmr**](https://github.com/gaearon/react-transform-hmr) - enables hot reloading using HMR API
26* [**react-transform-catch-errors**](https://github.com/gaearon/react-transform-catch-errors) - catches errors inside `render()`
27* [**react-transform-debug-inspector**](https://github.com/alexkuz/react-transform-debug-inspector) - renders an inline prop inspector
28* [**react-transform-render-visualizer**](https://github.com/spredfast/react-transform-render-visualizer) - highlight components when updated
29
30Feeling inspired? Learn [how to write transforms](#writing-transforms) and send a PR!
31
32## Demo Project
33
34Check out **[react-transform-boilerplate](https://github.com/gaearon/react-transform-boilerplate)** for a demo showing a combination of transforms.
35
36![](https://cloud.githubusercontent.com/assets/1539088/11611771/ae1a6bd8-9bac-11e5-9206-42447e0fe064.gif)
37
38## Installation
39
40This plugin is designed to be used with the Babel 6 ecosystem. These instructions assume you have a working project set up. If you do not have Babel set up in your project, [learn how to integrate](https://babeljs.io/docs/setup/) it with your toolkit before installing this plugin.
41
42##### Using NPM
43
44Install plugin and save in `devDependencies`:
45
46```bash
47npm install --save-dev babel-plugin-react-transform
48```
49
50Install some transforms:
51
52```bash
53npm install --save-dev react-transform-hmr
54npm install --save-dev react-transform-catch-errors
55```
56
57##### Configuration
58Add react-transform to the list of plugins in your babel configuration (usually `.babelrc`):
59
60```js
61
62
63{
64 "presets": ["react", "es2015"],
65 "env": {
66 // this plugin will be included only in development mode, e.g.
67 // if NODE_ENV (or BABEL_ENV) environment variable is not set
68 // or is equal to "development"
69 "development": {
70 "plugins": [
71 // must be an array with options object as second item
72 ["react-transform", {
73 // must be an array of objects
74 "transforms": [{
75 // can be an NPM module name or a local path
76 "transform": "react-transform-hmr",
77 // see transform docs for "imports" and "locals" dependencies
78 "imports": ["react"],
79 "locals": ["module"]
80 }, {
81 // you can have many transforms, not just one
82 "transform": "react-transform-catch-errors",
83 "imports": ["react", "redbox-react"]
84 }, {
85 // can be an NPM module name or a local path
86 "transform": "./src/my-custom-transform"
87 }]
88 // by default we only look for `React.createClass` (and ES6 classes)
89 // but you can tell the plugin to look for different component factories:
90 // factoryMethods: ["React.createClass", "createClass"]
91 }]
92 ]
93 }
94 }
95}
96```
97
98As you can see, each transform, apart from the `transform` field where you write it name, also has `imports` and `locals` fields. You should consult the docs of each individual transform to learn which `imports` and `locals` it might need, and how it uses them. You probably already guessed that this is just a way to inject local variables (like `module`) or dependencies (like `react`) into the transforms that need them.
99
100Note that when using `React.createClass()` and allowing `babel` to extract the `displayName` property you must ensure that [babel-plugin-react-display-name](https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-display-name) is included before `react-transform`. See [this github issue](https://github.com/gaearon/babel-plugin-react-transform/issues/19) for more details.
101
102You may optionally specify an array of strings called `factoryMethods` if you want the plugin to look for components created with a factory method other than `React.createClass`. Note that you don’t have to do anything special to look for ES6 components—`factoryMethods` is only relevant if you use factory methods akin to `React.createClass`.
103
104## Writing Transforms
105
106It’s not hard to write a custom transform! First, make sure you call your NPM package `react-transform-*` so we have uniform naming across the transforms. The only thing you should export from your transform module is a function.
107
108```js
109export default function myTransform() {
110 // ¯\_(ツ)_/¯
111}
112```
113
114This function should *return another function*:
115
116```js
117export default function myTransform() {
118 return function wrap(ReactClass) {
119 // ¯\_(ツ)_/¯
120 return ReactClass;
121 }
122}
123```
124
125As you can see, you’ll receive `ReactClass` as a parameter. It’s up to you to do something with it: monkeypatch its methods, create another component with the same prototype and a few different methods, wrap it into a higher-order component, etc. Be creative!
126
127```js
128export default function logAllUpdates() {
129 return function wrap(ReactClass) {
130 const displayName = // ¯\_(ツ)_/¯
131 const originalComponentDidUpdate = ReactClass.prototype.componentDidUpdate;
132
133 ReactClass.prototype.componentDidUpdate = function componentDidUpdate() {
134 console.info(`${displayName} updated:`, this.props, this.state);
135
136 if (originalComponentDidUpdate) {
137 originalComponentDidUpdate.apply(this, arguments);
138 }
139 }
140
141 return ReactClass;
142 }
143}
144```
145
146Oh, how do I get `displayName`?
147Actually, we give your transformation function a single argument called `options`. Yes, `options`:
148
149```js
150export default function logAllUpdates(options) {
151```
152
153It contains some useful data. For example, your `options` could look like this:
154
155```js
156{
157 // the file being processed
158 filename: '/Users/dan/p/my-projects/src/App.js',
159 // remember that "imports" .babelrc option?
160 imports: [React],
161 // remember that "locals" .babelrc option?
162 locals: [module],
163 // all components declared in the current file
164 components: {
165 $_MyComponent: {
166 // with their displayName when available
167 displayName: 'MyComponent'
168 },
169 $_SomeOtherComponent: {
170 displayName: 'SomeOtherComponent',
171 // and telling whether they are defined inside a function
172 isInFunction: true
173 }
174 }
175}
176```
177
178Of course, you might not want to use *all* options, but isn’t it nice to know that you have access to them in the top scope—which means before the component definitions actually run? (Hint: a hot reloading plugin might use this to decide whether a module is worthy of reloading, even if it contains an error and no React components have yet been wrapped because of it.)
179
180So, to retrieve the `displayName` (or `isInFunction`, when available), use the `options` parameter *and* the second `uniqueId` parameter given to the inner function after `ReactClass`:
181
182```js
183export default function logAllUpdates(options) {
184 return function wrap(ReactClass, uniqueId) {
185 const displayName = options.components[uniqueId].displayName || '<Unknown>';
186```
187
188This is it!
189
190Sure, it’s a slightly contrived example, as you can grab `ReactClass.displayName` just fine, but it illustrates a point: you have information about all of the components inside a file before that file executes, which is *very* handy for some transformations.
191
192Here is the complete code for this example transformation function:
193
194```js
195export default function logAllUpdates(options) {
196 return function wrap(ReactClass, uniqueId) {
197 const displayName = options.components[uniqueId].displayName || '<Unknown>';
198 const originalComponentDidUpdate = ReactClass.prototype.componentDidUpdate;
199
200 ReactClass.prototype.componentDidUpdate = function componentDidUpdate() {
201 console.info(`${displayName} updated:`, this.props, this.state);
202
203 if (originalComponentDidUpdate) {
204 originalComponentDidUpdate.apply(this, arguments);
205 }
206 }
207
208 return ReactClass;
209 }
210}
211```
212
213Now go ahead and write your own! Don’t forget to tag it with `react-transform` [keyword on npm](https://www.npmjs.com/browse/keyword/react-transform).
214
215## Patrons
216
217The work on React Transform, [React Hot Loader](https://github.com/gaearon/react-hot-loader), [Redux](https://github.com/rackt/redux), and related projects was [funded by the community](https://www.patreon.com/reactdx). Meet some of the outstanding companies that made it possible:
218
219* [Webflow](https://github.com/webflow)
220* [Ximedes](https://www.ximedes.com/)
221
222[See the full list of React Transform patrons.](PATRONS.md)
223
224## License
225
226MIT