@samitouri / QOS-React / commits / 9174c29acc

Reusable Components ES6 Update

Rafael Angeline committed May 22, 2016 at 18:08 UTC 9174c29acc60a7f2c1de1b729d7b2ff5492391f8
2 files changed +362 -213
docs/docs/05-reusable-components.md
+345 -202
@@ -10,90 +10,105 @@ When designing interfaces, break down the common design elements (buttons, form
10
11 ## Prop Validation
12
13 -As your app grows it's helpful to ensure that your components are used correctly. We do this by allowing you to specify `propTypes`. `React.PropTypes` exports a range of validators that can be used to make sure the data you receive is valid. When an invalid value is provided for a prop, a warning will be shown in the JavaScript console. Note that for performance reasons `propTypes` is only checked in development mode. Here is an example documenting the different validators provided:
13 +As your app grows it's helpful to ensure that your components are used correctly. We do this by allowing you to specify `propTypes`. `React.PropTypes` exports a range of validators that can be used to make sure the data you receive is valid. When an invalid value is provided for a prop, a warning will be shown in the JavaScript console. Note that for performance reasons `propTypes` is only checked in development mode.
14 +
15 +You can assign a special property to a component to declare its `propTypes`:
16
17 ```javascript
16 -React.createClass({
17 - propTypes: {
18 - // You can declare that a prop is a specific JS primitive. By default, these
19 - // are all optional.
20 - optionalArray: React.PropTypes.array,
21 - optionalBool: React.PropTypes.bool,
22 - optionalFunc: React.PropTypes.func,
23 - optionalNumber: React.PropTypes.number,
24 - optionalObject: React.PropTypes.object,
25 - optionalString: React.PropTypes.string,
26 - optionalSymbol: React.PropTypes.symbol,
27 -
28 - // Anything that can be rendered: numbers, strings, elements or an array
29 - // (or fragment) containing these types.
30 - optionalNode: React.PropTypes.node,
31 -
32 - // A React element.
33 - optionalElement: React.PropTypes.element,
34 -
35 - // You can also declare that a prop is an instance of a class. This uses
36 - // JS's instanceof operator.
37 - optionalMessage: React.PropTypes.instanceOf(Message),
38 -
39 - // You can ensure that your prop is limited to specific values by treating
40 - // it as an enum.
41 - optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
42 -
43 - // An object that could be one of many types
44 - optionalUnion: React.PropTypes.oneOfType([
45 - React.PropTypes.string,
46 - React.PropTypes.number,
47 - React.PropTypes.instanceOf(Message)
48 - ]),
49 -
50 - // An array of a certain type
51 - optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
52 -
53 - // An object with property values of a certain type
54 - optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
55 -
56 - // An object taking on a particular shape
57 - optionalObjectWithShape: React.PropTypes.shape({
58 - color: React.PropTypes.string,
59 - fontSize: React.PropTypes.number
60 - }),
61 -
62 - // You can chain any of the above with `isRequired` to make sure a warning
63 - // is shown if the prop isn't provided.
64 - requiredFunc: React.PropTypes.func.isRequired,
65 -
66 - // A value of any data type
67 - requiredAny: React.PropTypes.any.isRequired,
68 -
69 - // You can also specify a custom validator. It should return an Error
70 - // object if the validation fails. Don't `console.warn` or throw, as this
71 - // won't work inside `oneOfType`.
72 - customProp: function(props, propName, componentName) {
73 - if (!/matchme/.test(props[propName])) {
74 - return new Error(
75 - 'Invalid prop `' + propName + '` supplied to' +
76 - ' `' + componentName + '`. Validation failed.'
77 - );
78 - }
79 - },
80 -
81 - // You can also supply a custom validator to `arrayOf` and `objectOf`.
82 - // It should return an Error object if the validation fails. The validator
83 - // will be called for each key in the array or object. The first two
84 - // arguments of the validator are the array or object itself, and the
85 - // current item's key.
86 - customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
87 - if (!/matchme/.test(propValue[key])) {
88 - return new Error(
89 - 'Invalid prop `' + propFullName + '` supplied to' +
90 - ' `' + componentName + '`. Validation failed.'
91 - );
92 - }
93 - })
18 +class Greeting extends React.Component {
19 + render() {
20 + return (
21 + <h1>Hello, {this.props.name}</h1>
22 + );
23 + }
24 +}
25 +
26 +Greeting.propTypes = {
27 + name: React.PropTypes.string
28 +};
29 +```
30 +
31 +Here is an example documenting the different validators provided:
32 +
33 +```javascript
34 +MyComponent.propTypes = {
35 + // You can declare that a prop is a specific JS primitive. By default, these
36 + // are all optional.
37 + optionalArray: React.PropTypes.array,
38 + optionalBool: React.PropTypes.bool,
39 + optionalFunc: React.PropTypes.func,
40 + optionalNumber: React.PropTypes.number,
41 + optionalObject: React.PropTypes.object,
42 + optionalString: React.PropTypes.string,
43 + optionalSymbol: React.PropTypes.symbol,
44 +
45 + // Anything that can be rendered: numbers, strings, elements or an array
46 + // (or fragment) containing these types.
47 + optionalNode: React.PropTypes.node,
48 +
49 + // A React element.
50 + optionalElement: React.PropTypes.element,
51 +
52 + // You can also declare that a prop is an instance of a class. This uses
53 + // JS's instanceof operator.
54 + optionalMessage: React.PropTypes.instanceOf(Message),
55 +
56 + // You can ensure that your prop is limited to specific values by treating
57 + // it as an enum.
58 + optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
59 +
60 + // An object that could be one of many types
61 + optionalUnion: React.PropTypes.oneOfType([
62 + React.PropTypes.string,
63 + React.PropTypes.number,
64 + React.PropTypes.instanceOf(Message)
65 + ]),
66 +
67 + // An array of a certain type
68 + optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
69 +
70 + // An object with property values of a certain type
71 + optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
72 +
73 + // An object taking on a particular shape
74 + optionalObjectWithShape: React.PropTypes.shape({
75 + color: React.PropTypes.string,
76 + fontSize: React.PropTypes.number
77 + }),
78 +
79 + // You can chain any of the above with `isRequired` to make sure a warning
80 + // is shown if the prop isn't provided.
81 + requiredFunc: React.PropTypes.func.isRequired,
82 +
83 + // A value of any data type
84 + requiredAny: React.PropTypes.any.isRequired,
85 +
86 + // You can also specify a custom validator. It should return an Error
87 + // object if the validation fails. Don't `console.warn` or throw, as this
88 + // won't work inside `oneOfType`.
89 + customProp: function(props, propName, componentName) {
90 + if (!/matchme/.test(props[propName])) {
91 + return new Error(
92 + 'Invalid prop `' + propName + '` supplied to' +
93 + ' `' + componentName + '`. Validation failed.'
94 + );
95 + }
96 },
95 - /* ... */
96 -});
97 +
98 + // You can also supply a custom validator to `arrayOf` and `objectOf`.
99 + // It should return an Error object if the validation fails. The validator
100 + // will be called for each key in the array or object. The first two
101 + // arguments of the validator are the array or object itself, and the
102 + // current item's key.
103 + customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
104 + if (!/matchme/.test(propValue[key])) {
105 + return new Error(
106 + 'Invalid prop `' + propFullName + '` supplied to' +
107 + ' `' + componentName + '`. Validation failed.'
108 + );
109 + }
110 + })
111 +};
112 ```
113
114 ### Single Child
@@ -101,20 +116,21 @@ React.createClass({
116 With `React.PropTypes.element` you can specify that only a single child can be passed to a component as children.
117
118 ```javascript
104 -var MyComponent = React.createClass({
105 - propTypes: {
106 - children: React.PropTypes.element.isRequired
107 - },
108 -
109 - render: function() {
119 +class MyComponent extends React.Component {
120 + render() {
121 + // This must be exactly one element or it will warn.
122 + var children = this.props.children;
123 return (
124 <div>
112 - {this.props.children} // This must be exactly one element or it will warn.
125 + {children}
126 </div>
127 );
128 }
129 +}
130
117 -});
131 +MyComponent.propTypes = {
132 + children: React.PropTypes.element.isRequired
133 +};
134 ```
135
136 ## Default Prop Values
@@ -122,29 +138,41 @@ var MyComponent = React.createClass({
138 React lets you define default values for your `props` in a very declarative way:
139
140 ```javascript
125 -var ComponentWithDefaultProps = React.createClass({
126 - getDefaultProps: function() {
127 - return {
128 - value: 'default value'
129 - };
141 +class Greeting extends React.Component {
142 + render() {
143 + return (
144 + <h1>Hello, {this.props.name}</h1>
145 + );
146 }
131 - /* ... */
132 -});
147 +}
148 +
149 +// Specifies the default values for props:
150 +Greeting.defaultProps = {
151 + name: 'Stranger'
152 +};
153 +
154 +// Renders "Hello, Stranger":
155 +ReactDOM.render(
156 + <Greeting />,
157 + document.getElementById('example')
158 +);
159 ```
160
135 -The result of `getDefaultProps()` will be cached and used to ensure that `this.props.value` will have a value if it was not specified by the owner component. This allows you to safely just use your props without having to write repetitive and fragile code to handle that yourself.
161 +The `defaultProps` will be used to ensure that `this.props.name` will have a value if it was not specified by the parent component. This allows you to safely just use your props without having to write repetitive and fragile code to handle that yourself.
162
163 ## Transferring Props: A Shortcut
164
165 A common type of React component is one that extends a basic HTML element in a simple way. Often you'll want to copy any HTML attributes passed to your component to the underlying HTML element. To save typing, you can use the JSX _spread_ syntax to achieve this:
166
167 ```javascript
142 -var CheckLink = React.createClass({
143 - render: function() {
168 +class CheckLink extends React.Component {
169 + render() {
170 // This takes any props passed to CheckLink and copies them to <a>
145 - return <a {...this.props}>{'√ '}{this.props.children}</a>;
171 + return (
172 + <a {...this.props}>{'√ '}{this.props.children}</a>
173 + );
174 }
147 -});
175 +}
176
177 ReactDOM.render(
178 <CheckLink href="/checked.html">
@@ -154,166 +182,281 @@ ReactDOM.render(
182 );
183 ```
184
157 -## Mixins
185 +## Stateless Functions
186
159 -Components are the best way to reuse code in React, but sometimes very different components may share some common functionality. These are sometimes called [cross-cutting concerns](https://en.wikipedia.org/wiki/Cross-cutting_concern). React provides `mixins` to solve this problem.
187 +If a component doesn't use local state or lifecycle hooks, you can define it as a function instead of a class:
188
161 -One common use case is a component wanting to update itself on a time interval. It's easy to use `setInterval()`, but it's important to cancel your interval when you don't need it anymore to save memory. React provides [lifecycle methods](/react/docs/working-with-the-browser.html#component-lifecycle) that let you know when a component is about to be created or destroyed. Let's create a simple mixin that uses these methods to provide an easy `setInterval()` function that will automatically get cleaned up when your component is destroyed.
189 +```javascript
190 +function Greeting(props) {
191 + return <h1>Hello, {props.name}</h1>;
192 +}
193 +
194 +ReactDOM.render(
195 + <Greeting name="Sebastian" />,
196 + document.getElementById('example')
197 +);
198 +```
199 +
200 +Or using the new ES6 arrow syntax:
201
202 ```javascript
164 -var SetIntervalMixin = {
165 - componentWillMount: function() {
166 - this.intervals = [];
167 - },
168 - setInterval: function() {
169 - this.intervals.push(setInterval.apply(null, arguments));
170 - },
171 - componentWillUnmount: function() {
172 - this.intervals.forEach(clearInterval);
173 - }
203 +const Greeting = (props) => (
204 + <h1>Hello, {props.name}</h1v>
205 +);
206 +
207 +ReactDOM.render(
208 + <Greeting name="Sebastian" />,
209 + document.getElementById('example')
210 +);
211 +```
212 +
213 +This simplified component API is intended for components that are pure functions of their props. These components must not retain internal state, do not have backing instances, and do not have the component lifecycle methods. They are pure functional transforms of their input, with zero boilerplate.
214 +
215 +However, you may still specify `.propTypes` and `.defaultProps` by setting them as properties on the function, just as you would set them on an ES6 class:
216 +
217 +```javascript
218 +function Greeting(props) {
219 + return (
220 + <h1>Hello, {props.name}</h1v>
221 + );
222 +}
223 +
224 +Greeting.propTypes = {
225 + name: React.PropTypes.string
226 };
227
176 -var TickTock = React.createClass({
177 - mixins: [SetIntervalMixin], // Use the mixin
178 - getInitialState: function() {
179 - return {seconds: 0};
180 - },
181 - componentDidMount: function() {
182 - this.setInterval(this.tick, 1000); // Call a method on the mixin
183 - },
184 - tick: function() {
185 - this.setState({seconds: this.state.seconds + 1});
186 - },
187 - render: function() {
188 - return (
189 - <p>
190 - React has been running for {this.state.seconds} seconds.
191 - </p>
192 - );
193 - }
194 -});
228 +Greeting.defaultProps = {
229 + name: 'John Doe'
230 +};
231
232 ReactDOM.render(
197 - <TickTock />,
233 + <Greeting name="Mădălina"/>,
234 document.getElementById('example')
235 );
236 ```
237
202 -A nice feature of mixins is that if a component is using multiple mixins and several mixins define the same lifecycle method (i.e. several mixins want to do some cleanup when the component is destroyed), all of the lifecycle methods are guaranteed to be called. Methods defined on mixins run in the order mixins were listed, followed by a method call on the component.
238 +>**Note:**
239 +>
240 +> Because stateless functions don't have a backing instance, you can't attach a ref to a stateless function component. Normally this isn't an issue, since stateless functions do not provide an imperative API. Without an imperative API, there isn't much you could do with an instance anyway. However, if a user wants to find the DOM node of a stateless function component, they must wrap the component in a stateful component (eg. ES6 class component) and attach the ref to the stateful wrapper component.
241 +
242 +In an ideal world, many of your components would be stateless functions. In the future we plan to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations.
243 +
244 +When you don't need local state or lifecycle hooks in a component, we recommend declaring it with a function. Otherwise, we recommend to use the ES6 class syntax.
245
204 -## ES6 Classes
246 +## ES6 Classes and React.createClass()
247
206 -You may also define your React classes as a plain JavaScript class. For example using ES6 class syntax:
248 +Normally you would define a React component as a plain JavaScript class:
249
250 ```javascript
209 -class HelloMessage extends React.Component {
251 +class Greeting extends React.Component {
252 render() {
211 - return <div>Hello {this.props.name}</div>;
253 + return <h1>Hello, {this.props.name}</h1>;
254 }
255 }
214 -ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
256 ```
257
217 -The API is similar to `React.createClass` with the exception of `getInitialState`. Instead of providing a separate `getInitialState` method, you set up your own `state` property in the constructor. Just like the return value of `getInitialState`, the value you assign to `this.state` will be used as the initial state for your component.
258 +If you don't use ES6 yet, you may use [`React.createClass`](/react/docs/top-level-api.html#react.createclass) helper instead:
259
219 -Another difference is that `propTypes` and `defaultProps` are defined as properties on the constructor instead of in the class body.
260
261 ```javascript
222 -export class Counter extends React.Component {
223 - constructor(props) {
224 - super(props);
225 - this.state = {count: props.initialCount};
226 - this.tick = this.tick.bind(this);
227 - }
228 - tick() {
229 - this.setState({count: this.state.count + 1});
230 - }
231 - render() {
232 - return (
233 - <div onClick={this.tick}>
234 - Clicks: {this.state.count}
235 - </div>
236 - );
262 +var Greeting = React.createClass({
263 + render: function() {
264 + return <h1>Hello, {this.props.name}</h1>;
265 }
238 -}
239 -Counter.propTypes = { initialCount: React.PropTypes.number };
240 -Counter.defaultProps = { initialCount: 0 };
266 +});
267 ```
268
243 -### No Autobinding
269 +The API of ES6 classes is similar to [`React.createClass`](/react/docs/top-level-api.html#react.createclass) with a few exceptions.
270 +
271 +### Declaring Prop Types and Default Props
272
245 -Methods follow the same semantics as regular ES6 classes, meaning that they don't automatically bind `this` to the instance. You'll have to explicitly use `.bind(this)` or [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) `=>`:
273 +With functions and ES6 classes, `propTypes` and `defaultProps` are defined as properties on the components themselves:
274
275 ```javascript
248 -// You can use bind() to preserve `this`
249 -<div onClick={this.tick.bind(this)}>
276 +class Greeting extends React.Component {
277 + // ...
278 +}
279
251 -// Or you can use arrow functions
252 -<div onClick={() => this.tick()}>
280 +Greeting.propTypes = {
281 + name: React.PropTypes.string
282 +};
283 +
284 +Greeting.defaultProps = {
285 + name: 'Mary'
286 +};
287 ```
288
255 -We recommend that you bind your event handlers in the constructor so they are only bound once for every instance:
289 +With `React.createClass()`, you need to define `propTypes` as a property on the passed object, and `getDefaultProps()` as a function on it:
290
291 ```javascript
258 -constructor(props) {
259 - super(props);
260 - this.state = {count: props.initialCount};
261 - this.tick = this.tick.bind(this);
262 -}
292 +var Greeting = React.createClass({
293 + propTypes: {
294 + name: React.PropTypes.string
295 + },
296 +
297 + getDefaultProps: function() {
298 + return {
299 + name: 'Mary'
300 + };
301 + },
302 +
303 + // ...
304 +
305 +});
306 ```
307
265 -Now you can use `this.tick` directly as it was bound once in the constructor:
308 +### Setting the Initial State
309 +
310 +In ES6 classes, you can define the initial state by assigning `this.state` in the constructor:
311
312 ```javascript
268 -// It is already bound in the constructor
269 -<div onClick={this.tick}>
313 +class Counter extends React.Component {
314 + constructor(props) {
315 + super(props);
316 + this.state = {count: props.initialCount};
317 + }
318 + // ...
319 +}
320 ```
321
272 -This is better for performance of your application, especially if you implement [shouldComponentUpdate()](/react/docs/component-specs.html#updating-shouldcomponentupdate) with a [shallow comparison](/react/docs/shallow-compare.html) in the child components.
273 -
274 -### No Mixins
322 +With `React.createClass()`, you have to provide a separate `getInitialState` method that returns the initial state:
323
276 -Unfortunately ES6 launched without any mixin support. Therefore, there is no support for mixins when you use React with ES6 classes. Instead, we're working on making it easier to support such use cases without resorting to mixins.
324 +```javascript
325 +var Counter = React.createClass({
326 + getInitialState: function() {
327 + return {count: props.initialCount};
328 + },
329 + // ...
330 +});
331 +```
332
278 -## Stateless Functions
333 +### Autobinding
334
280 -You may also define your React classes as a plain JavaScript function. For example using the stateless function syntax:
335 +In React components declared as ES6 classes, methods follow the same semantics as regular ES6 classes. This means that they don't automatically bind `this` to the instance. You'll have to explicitly use `.bind(this)` in the constructor:
336
337 ```javascript
283 -function HelloMessage(props) {
284 - return <div>Hello {props.name}</div>;
338 +class SayHello extends React.Component {
339 + constructor(props) {
340 + super(props);
341 + // This line is important!
342 + this.handleClick = this.handleClick.bind(this);
343 + }
344 +
345 + handleClick() {
346 + alert('Hello!');
347 + }
348 +
349 + render() {
350 + // Because we `this.tick` is bound, we can use it as an event handler.
351 + return (
352 + <button onClick={this.handleClick}>
353 + Say hello
354 + </button>
355 + );
356 + }
357 }
286 -ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
358 ```
359
289 -Or using the new ES6 arrow syntax:
360 +With `React.createClass()`, this is not necessary because it binds all methods:
361
362 ```javascript
292 -const HelloMessage = (props) => <div>Hello {props.name}</div>;
293 -ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
363 +var SayHello = React.createClass({
364 + handleClick: function() {
365 + alert('Hello!');
366 + },
367 +
368 + render: function() {
369 + return (
370 + <button onClick={this.handleClick}>
371 + Say hello
372 + </button>
373 + );
374 + }
375 +});
376 ```
377
296 -This simplified component API is intended for components that are pure functions of their props. These components must not retain internal state, do not have backing instances, and do not have the component lifecycle methods. They are pure functional transforms of their input, with zero boilerplate.
378 +This means writing ES6 classes comes with a little more boilerplate code for event handlers, but the upside is slightly better performance in large applications.
379 +
380 +If the boilerplate code is too unattractive to you, you may enable the **experimental** [Class Properties](https://babeljs.io/docs/plugins/transform-class-properties/) syntax proposal with Babel:
381
298 -However, you may still specify `.propTypes` and `.defaultProps` by setting them as properties on the function, just as you would set them on an ES6 class:
382
383 ```javascript
301 -const HelloMessage = (props) => <div>Hello, {props.name}</div>;
302 -HelloMessage.propTypes = {
303 - name: React.PropTypes.string
304 -}
305 -HelloMessage.defaultProps = {
306 - name: 'John Doe'
384 +class SayHello extends React.Component {
385 + // WARNING: this syntax is experimental!
386 + // Using an arrow here binds the method:
387 + handleClick = () => {
388 + alert('Hello!');
389 + }
390 +
391 + render() {
392 + return (
393 + <button onClick={this.handleClick}>
394 + Say hello
395 + </button>
396 + );
397 + }
398 }
308 -ReactDOM.render(<HelloMessage name="Mădălina"/>, mountNode);
399 ```
400
311 -> NOTE:
312 ->
313 -> Because stateless functions don't have a backing instance, you can't attach a ref to a stateless function component. Normally this isn't an issue, since stateless functions do not provide an imperative API. Without an imperative API, there isn't much you could do with an instance anyway. However, if a user wants to find the DOM node of a stateless function component, they must wrap the component in a stateful component (eg. ES6 class component) and attach the ref to the stateful wrapper component.
401 +Please note that the syntax above is **experimental** and the syntax may change, or the proposal might not make it into the language.
402 +
403 +If you'd rather play it safe, you have a few options:
404 +
405 +* Bind methods in the constructor.
406 +* Use arrow functions, e.g. `onClick={(e) => this.handleClick(e)})`.
407 +* Keep using `React.createClass()`.
408
315 -> NOTE:
409 +### Mixins
410 +
411 +>**Note:**
412 +>
413 +>ES6 launched without any mixin support. Therefore, there is no support for mixins when you use React with ES6 classes.
414 +>
415 +>**We also found numerous issues in codebases using mixins, [and don't recommend using them in the new code](/react/blog/2016/07/13/mixins-considered-harmful.html).**
416 >
317 -> In React v0.14, stateless functional components were not permitted to return `null` or `false` (a workaround is to return a `<noscript />` instead). This was fixed in React v15, and stateless functional components are now permitted to return `null`.
417 +>This section exists only for the reference.
418 +
419 +Sometimes very different components may share some common functionality. These are sometimes called [cross-cutting concerns](https://en.wikipedia.org/wiki/Cross-cutting_concern). [`React.createClass`](/react/docs/top-level-api.html#react.createclass) lets you use a legacy `mixins` system for that.
420 +
421 +One common use case is a component wanting to update itself on a time interval. It's easy to use `setInterval()`, but it's important to cancel your interval when you don't need it anymore to save memory. React provides [lifecycle methods](/react/docs/working-with-the-browser.html#component-lifecycle) that let you know when a component is about to be created or destroyed. Let's create a simple mixin that uses these methods to provide an easy `setInterval()` function that will automatically get cleaned up when your component is destroyed.
422 +
423 +```javascript
424 +var SetIntervalMixin = {
425 + componentWillMount: function() {
426 + this.intervals = [];
427 + },
428 + setInterval: function() {
429 + this.intervals.push(setInterval.apply(null, arguments));
430 + },
431 + componentWillUnmount: function() {
432 + this.intervals.forEach(clearInterval);
433 + }
434 +};
435 +
436 +var TickTock = React.createClass({
437 + mixins: [SetIntervalMixin], // Use the mixin
438 + getInitialState: function() {
439 + return {seconds: 0};
440 + },
441 + componentDidMount: function() {
442 + this.setInterval(this.tick, 1000); // Call a method on the mixin
443 + },
444 + tick: function() {
445 + this.setState({seconds: this.state.seconds + 1});
446 + },
447 + render: function() {
448 + return (
449 + <p>
450 + React has been running for {this.state.seconds} seconds.
451 + </p>
452 + );
453 + }
454 +});
455 +
456 +ReactDOM.render(
457 + <TickTock />,
458 + document.getElementById('example')
459 +);
460 +```
461
319 -In an ideal world, most of your components would be stateless functions because in the future we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations. This is the recommended pattern, when possible.
462 +If a component is using multiple mixins and several mixins define the same lifecycle method (i.e. several mixins want to do some cleanup when the component is destroyed), all of the lifecycle methods are guaranteed to be called. Methods defined on mixins run in the order mixins were listed, followed by a method call on the component.
docs/docs/07-forms.md
+17 -11
@@ -37,7 +37,7 @@ Like all DOM events, the `onChange` prop is supported on all native components a
37 A **controlled** `<input>` has a `value` prop. Rendering a controlled `<input>` will reflect the value of the `value` prop.
38
39 ```javascript
40 - render: function() {
40 + render() {
41 return <input type="text" value="Hello!" />;
42 }
43 ```
@@ -45,13 +45,18 @@ A **controlled** `<input>` has a `value` prop. Rendering a controlled `<input>`
45 User input will have no effect on the rendered element because React has declared the value to be `Hello!`. To update the value in response to user input, you could use the `onChange` event:
46
47 ```javascript
48 - getInitialState: function() {
49 - return {value: 'Hello!'};
50 - },
51 - handleChange: function(event) {
48 +class MyForm extends React.Component {
49 + constructor(props) {
50 + super(props);
51 + this.state = {value: 'Hello!'};
52 + this.handleChange = this.handleChange.bind(this);
53 + }
54 +
55 + handleChange(event) {
56 this.setState({value: event.target.value});
53 - },
54 - render: function() {
57 + }
58 +
59 + render() {
60 return (
61 <input
62 type="text"
@@ -60,12 +65,13 @@ User input will have no effect on the rendered element because React has declare
65 />
66 );
67 }
68 +}
69 ```
70
71 In this example, we are accepting the value provided by the user and updating the `value` prop of the `<input>` component. This pattern makes it easy to implement interfaces that respond to or validate user interactions. For example:
72
73 ```javascript
68 - handleChange: function(event) {
74 + handleChange(event) {
75 this.setState({value: event.target.value.substr(0, 140)});
76 }
77 ```
@@ -83,7 +89,7 @@ Be aware that, in an attempt to normalize change handling for checkbox and radio
89 An `<input>` without a `value` property is an *uncontrolled* component:
90
91 ```javascript
86 - render: function() {
92 + render() {
93 return <input type="text" />;
94 }
95 ```
@@ -97,7 +103,7 @@ An **uncontrolled** component maintains its own internal state.
103 If you want to initialize the component with a non-empty value, you can supply a `defaultValue` prop. For example:
104
105 ```javascript
100 - render: function() {
106 + render() {
107 return <input type="text" defaultValue="Hello!" />;
108 }
109 ```
@@ -125,7 +131,7 @@ This renders an input *initialized* with the value, `Untitled`. When the user up
131 Unlike HTML, React components must represent the state of the view at any point in time and not only at initialization time. For example, in React:
132
133 ```javascript
128 - render: function() {
134 + render() {
135 return <input type="text" name="title" value="Untitled" />;
136 }
137 ```