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 |
|
12 | This 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 |
|
21 | For 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 |
|
30 | Feeling inspired? Learn [how to write transforms](#writing-transforms) and send a PR!
|
31 |
|
32 | ## Demo Project
|
33 |
|
34 | Check 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 |
|
40 | This 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 |
|
44 | Install plugin and save in `devDependencies`:
|
45 |
|
46 | ```bash
|
47 | npm install --save-dev babel-plugin-react-transform
|
48 | ```
|
49 |
|
50 | Install some transforms:
|
51 |
|
52 | ```bash
|
53 | npm install --save-dev react-transform-hmr
|
54 | npm install --save-dev react-transform-catch-errors
|
55 | ```
|
56 |
|
57 | ##### Configuration
|
58 | Add 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 |
|
98 | As 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 |
|
100 | Note 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 |
|
102 | You 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 |
|
106 | It’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
|
109 | export default function myTransform() {
|
110 | // ¯\_(ツ)_/¯
|
111 | }
|
112 | ```
|
113 |
|
114 | This function should *return another function*:
|
115 |
|
116 | ```js
|
117 | export default function myTransform() {
|
118 | return function wrap(ReactClass) {
|
119 | // ¯\_(ツ)_/¯
|
120 | return ReactClass;
|
121 | }
|
122 | }
|
123 | ```
|
124 |
|
125 | As 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
|
128 | export 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 |
|
146 | Oh, how do I get `displayName`?
|
147 | Actually, we give your transformation function a single argument called `options`. Yes, `options`:
|
148 |
|
149 | ```js
|
150 | export default function logAllUpdates(options) {
|
151 | ```
|
152 |
|
153 | It 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 |
|
178 | Of 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 |
|
180 | So, 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
|
183 | export default function logAllUpdates(options) {
|
184 | return function wrap(ReactClass, uniqueId) {
|
185 | const displayName = options.components[uniqueId].displayName || '<Unknown>';
|
186 | ```
|
187 |
|
188 | This is it!
|
189 |
|
190 | Sure, 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 |
|
192 | Here is the complete code for this example transformation function:
|
193 |
|
194 | ```js
|
195 | export 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 |
|
213 | Now 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 |
|
217 | The 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 |
|
226 | MIT
|