petehunt
12 years ago
8 changed files with 518 additions and 21 deletions
@ -0,0 +1,86 @@ |
|||
## Build reusable component libraries! |
|||
|
|||
When designing interfaces, break down the common design elements (buttons, form fields, layout components, etc) into reusable components with well-defined interfaces. That way, the next time you need to build some UI you can write much less code, which means faster development time, less bugs, and less bytes down the wire. |
|||
|
|||
## Prop validation |
|||
|
|||
As your app grows it's helpful to ensure that your components are used correctly. We do this using `propTypes`. |
|||
|
|||
** TODO zpao ** |
|||
|
|||
## Transferring props: a shortcut |
|||
|
|||
A common type of React component is one that extends a basic HTML 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. React provides `transferPropsTo()` to do just this. |
|||
|
|||
```javascript |
|||
/** @jsx React.DOM */ |
|||
|
|||
var CheckLink = React.createClass({ |
|||
render: function() { |
|||
// transferPropsTo() will take any props pased to CheckLink |
|||
// and copy them to <a> |
|||
return this.transferPropsTo(<a>{'√ '}{this.props.children}</a>); |
|||
} |
|||
}); |
|||
|
|||
React.renderComponent( |
|||
<CheckLink href="javascript:alert('Hello, world!');"> |
|||
Click here! |
|||
</CheckLink>, |
|||
document.getElementById('example') |
|||
); |
|||
``` |
|||
|
|||
## Mixins |
|||
|
|||
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](http://en.wikipedia.org/wiki/Cross-cutting_concern). React provides `mixins` to solve this problem. |
|||
|
|||
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](./06-working-with-the-browser.html) 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. |
|||
|
|||
```javascript |
|||
/** @jsx React.DOM */ |
|||
|
|||
var SetIntervalMixin = { |
|||
componentWillMount: function() { |
|||
this.intervals = []; |
|||
}, |
|||
setInterval: function() { |
|||
this.intervals.push(setInterval.apply(null, arguments)); |
|||
}, |
|||
componentWillUnmount: function() { |
|||
this.intervals.map(clearInterval); |
|||
} |
|||
}; |
|||
|
|||
var TickTock = React.createClass({ |
|||
mixins: [SetIntervalMixin], // Use the mixin |
|||
getInitialState: function() { |
|||
return {seconds: 0}; |
|||
}, |
|||
componentDidMount: function() { |
|||
this.setInterval(this.tick, 1000); // Call a method on the mixin |
|||
}, |
|||
tick: function() { |
|||
this.setState({seconds: this.state.seconds + 1}); |
|||
}, |
|||
render: function() { |
|||
return ( |
|||
<p> |
|||
React has been running for {this.state.seconds} seconds. |
|||
</p> |
|||
); |
|||
} |
|||
}); |
|||
|
|||
React.renderComponent( |
|||
<TickTock />, |
|||
document.getElementById('example') |
|||
); |
|||
``` |
|||
|
|||
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. |
|||
|
|||
## Testing |
|||
|
|||
**TODO: benjamn** |
|||
|
@ -0,0 +1,119 @@ |
|||
# Forms |
|||
|
|||
Form components such as `<input>`, `<textarea>`, and `<option>` differ from other native components because they can be mutated via user interactions. These components provide interfaces that make it easier to manage forms in response to user interactions. |
|||
|
|||
## Interactive Props |
|||
|
|||
Form components support a few props that are affected via user interactions: |
|||
|
|||
- `value`, supported by `<input>` and `<textarea>` components. |
|||
- `checked`, supported by `<input>` components of type `checkbox` or `radio`. |
|||
- `selected`, supported by `<option>` components. |
|||
|
|||
In HTML, the value of `<textarea>` is set via children. In React, you should use `value` instead. |
|||
|
|||
Form components allow listening for changes by setting a callback to the `onChange` prop. The `onChange` prop works across browsers to fire in response to user interactions when: |
|||
|
|||
- The `value` of `<input>` or `<textarea>` changes. |
|||
- The `checked` state of `<input>` changes. |
|||
- The `selected` state of `<option>` changes. |
|||
|
|||
Like all DOM events, the `onChange` prop is supported on all native components and can be used to listen to bubbled change events. |
|||
|
|||
## Controlled Components |
|||
|
|||
An `<input>` with `value` set is a *controlled* component. In a controlled `<input>`, the value of the rendered element will always reflect the `value` prop. For example: |
|||
|
|||
```javascript |
|||
render: function() { |
|||
return <input type="text" value="Hello!" />; |
|||
} |
|||
``` |
|||
|
|||
This will render an input that always has a value of `Hello!`. Any user input will have no effect on the rendered element because React has declared the value to be `Hello!`. If you wanted to update the value in response to user input, you could use the `onChange` event: |
|||
|
|||
```javascript |
|||
getInitialState: function() { |
|||
return {value: 'Hello!'}; |
|||
}, |
|||
render: function() { |
|||
var value = this.state.value; |
|||
return <input type="text" value={value} onChange={this.handleChange} />; |
|||
}, |
|||
handleChange: function(event) { |
|||
this.setState({value: event.target.value}); |
|||
} |
|||
``` |
|||
|
|||
In this example, we are simply accepting the newest 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: |
|||
|
|||
```javascript |
|||
handleChange: function(event) { |
|||
this.setState({value: event.target.value.substr(0, 140)}); |
|||
} |
|||
``` |
|||
|
|||
This would accept user input but truncate the value to the first 140 characters. |
|||
|
|||
## Uncontrolled Components |
|||
|
|||
An `<input>` that does not supply a `value` (or sets it to `null`) is an *uncontrolled* component. In an uncontrolled `<input>`, the value of the rendered element will reflect the user's input. For example: |
|||
|
|||
```javascript |
|||
render: function() { |
|||
return <input type="text" />; |
|||
} |
|||
``` |
|||
|
|||
This will render an input that starts off with an empty value. Any user input will be immediately reflected by the rendered element. If you wanted to listen to updates to the value, you could use the `onChange` event just like you can with controlled components. |
|||
|
|||
If you want to initialize the component with a non-empty value, you can supply a `defaultValue` prop. For example: |
|||
|
|||
```javascript |
|||
render: function() { |
|||
return <input type="text" defaultValue="Hello!" />; |
|||
} |
|||
``` |
|||
|
|||
This example will function much like the **Controlled Components** example above. |
|||
|
|||
Likewise, `<input>` supports `defaultChecked` and `<option>` supports `defaultSelected`. |
|||
|
|||
## Advanced Topics |
|||
|
|||
### Why controlled components? |
|||
|
|||
Using form components such as `<input>` in React presents a challenge that is absent when writing traditional form HTML. For example, in HTML: |
|||
|
|||
```html |
|||
<input type="text" name="title" value="Untitled" /> |
|||
``` |
|||
|
|||
This renders an input *initialized* with the value, `Untitled`. When the user updates the input, the node's value *property* will change. However, `node.getAttribute('value')` will still return the value used at initialization time, `Untitled`. |
|||
|
|||
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: |
|||
|
|||
```javascript |
|||
render: function() { |
|||
return <input type="text" name="title" value="Untitled" />; |
|||
} |
|||
``` |
|||
|
|||
Since this method describes the view at any point in time, the value of the text input should *always* be `Untitled`. |
|||
|
|||
### Why textarea value? |
|||
|
|||
In HTML, the value of `<textarea>` is usually set using its children: |
|||
|
|||
```html |
|||
<!-- counterexample: DO NOT DO THIS! --> |
|||
<textarea name="description">This is the description.</textarea> |
|||
``` |
|||
|
|||
For HTML, this easily allows developers to supply multiline values. However, since React is JavaScript, we do not have string limitations and can use `\n` if we want newlines. In a world where we have `value` and `defaultValue`, it is ambiguous what role children play. For this reason, you should not use children when setting `<textarea>` values: |
|||
|
|||
```javascript |
|||
<textarea name="description" value="This is a description." /> |
|||
``` |
|||
|
|||
If you *do* decide to use children, they will behave like `defaultValue`. |
@ -0,0 +1,113 @@ |
|||
# Working with the browser |
|||
|
|||
React provides powerful abstractions that free you from touching the DOM directly in most cases, but sometimes you simply need to access the underlying API, perhaps to work with a third-party library. |
|||
|
|||
## The mock DOM |
|||
|
|||
React is so fast because it never talks to the DOM directly. React maintains a fast in-memory representation of the DOM. `render()` methods return a *description* of the DOM, and React can diff this description with the in-memory representation to compute the fastest way to update the browser. |
|||
|
|||
Additionally, React implements a full synthetic event system such that all event objects are guaranteed to conform to the W3C spec despite browser quirks, and everything bubbles consistently and in a performant way cross-browser. You can even use some HTML5 events in IE8! |
|||
|
|||
Most of the time you should stay within React's "faked browser" world since it's more performant and easier to reason about. However, sometimes you simply need to access the underlying API, perhaps to work with a third-party library like a jQuery plugin. React provides escape hatches for you to use the underlying DOM API directly. |
|||
|
|||
## Refs and getDOMNode() |
|||
|
|||
To interact with the browser, you'll need a reference to a DOM node. Every mounted React component has a `getDOMNode()` function which you can call to get a reference to it. |
|||
|
|||
**Warning:** `getDOMNode()` only works on mounted components (that is, components that have been placed in the DOM). If you try to call this on a component that has not been mounted yet (like calling `getDOMNode()` in `render()` on a component that has yet to be created) an exception will be thrown. |
|||
|
|||
In order to get a reference to a React component, you can either use `this` to get the current React component, or you can use refs to refer to a component you own. They work like this: |
|||
|
|||
```javascript |
|||
/** @jsx React.DOM */ |
|||
|
|||
var MyComponent = React.createClass({ |
|||
handleClick: function() { |
|||
// Explicitly focus the text input using the raw DOM API. |
|||
this.refs.myTextInput.getDOMNode().focus(); |
|||
}, |
|||
render: function() { |
|||
// The ref attribute adds a reference to the component to |
|||
// this.refs when the component is mounted. |
|||
return ( |
|||
<div> |
|||
<input type="text" ref="myTextInput" /> |
|||
<input |
|||
type="button" |
|||
value="Focus the text input" |
|||
onClick={this.handleClick} |
|||
/> |
|||
</div> |
|||
); |
|||
} |
|||
}); |
|||
|
|||
React.renderComponent( |
|||
<MyComponent />, |
|||
document.getElementById('example') |
|||
); |
|||
``` |
|||
|
|||
## More about refs |
|||
|
|||
To learn more about refs, including ways to use them effectively, see our [more about refs](./07.1-more-about-refs.md) documentation. |
|||
|
|||
## Component lifecycle |
|||
|
|||
Components have three main parts of their lifecycle: |
|||
- **Mounting:** A component is being inserted into the DOM. |
|||
- **Updating:** A component is being re-rendered to determine if the DOM should be updated. |
|||
- **Unmounting:** A component is being removed from the DOM. |
|||
|
|||
React provides lifecycle methods that you can override to hook into this process. We provide **will** methods, which are called right before something happens, and **did** methods which are called right after something happens. |
|||
|
|||
### Mounting |
|||
|
|||
- `getInitialState(): object` is invoked before a component is mounted. Stateful components should implement this and return the initial state data. |
|||
- `componentWillMount()` is invoked immediately before mounting occurs. |
|||
- `componentDidMount(DOMElement rootNode)` is invoked immediately after mounting occurs. Initialization that requires DOM nodes should go here. |
|||
|
|||
### Updating |
|||
|
|||
- `componentWillReceiveProps(object nextProps)` is invoked when a mounted component receives new props. This method should be used to compare `this.props` and `nextProps` to perform state transitions using `this.setState()`. |
|||
- `shouldComponentUpdate(object nextProps, object nextState): boolean` is invoked when a component decides whether any changes warrant an update to the DOM. Implement this as an optimization to compare `this.props` with `nextProps` and `this.state` with `nextState` and return false if React should skip updating. |
|||
- `componentWillUpdate(object nextProps, object nextState)` is invoked immediately before updating occurs. You cannot call `this.setState()` here. |
|||
- `componentDidUpdate(object prevProps, object prevState, DOMElement rootNode)` is invoked immediately after updating occurs. |
|||
|
|||
### Unmounting |
|||
|
|||
- `componentWillUnmount()` is invoked immediately before a component is unmounted and destroyed. Cleanup should go here. |
|||
|
|||
### Mounted Methods |
|||
|
|||
_Mounted_ composite components also support the following methods: |
|||
|
|||
- `getDOMNode(): DOMElement` can be invoked on any mounted component in order to obtain a reference to its rendered DOM node. |
|||
- `forceUpdate()` can be invoked on any mounted component when you know that some deeper aspect of the component's state has changed without using `this.setState()`. |
|||
|
|||
> Note: |
|||
> |
|||
> The `DOMElement rootNode` argument of `componentDidMount()` and |
|||
> `componentDidUpdate()` is a convenience. The same node can be obtained by |
|||
> calling `this.getDOMNode()`. |
|||
|
|||
## Browser suppport and polyfills |
|||
|
|||
At Facebook, we support older browsers, including IE8. We've had polyfills in place for a long time to allow us to write forward-thinking JS. This means we don't have a bunch of hacks scattered throughout our codebase and we can still expect our code to "just work". For example, instead of seeing `+new Date()`, we can just write `Date.now()`. Since the open source React is the same as what we use internally, we've carried over this philosophy of using forward thinking JS. |
|||
|
|||
In addition to that philosphy, we've also taken the stance that we, as authors of a JS library, should not be shipping polyfills as a part of our library. If every library did this, there's a good chance you'd be sending down the same polyfill multiple times, which could be a sizable chunk of dead code. If your product needs to support older browsers, chances are you're already using something like [es5-shim](https://github.com/kriskowal/es5-shim). |
|||
|
|||
|
|||
### Polyfills needed to support older browsers |
|||
|
|||
* `Array.isArray` |
|||
* `Array.prototype.forEach` |
|||
* `Array.prototype.indexOf` |
|||
* `Function.prototype.bind` |
|||
* `Date.now` |
|||
* `Array.prototype.some` (also in `es5-shim.js`) |
|||
|
|||
All of these can be polyfilled using `es5-shim.js` from https://github.com/kriskowal/es5-shim. |
|||
|
|||
* `console.*` - https://github.com/paulmillr/console-polyfill |
|||
* `Object.create` - Provided in `es5-sham.js` @ https://github.com/kriskowal/es5-shim |
@ -0,0 +1,130 @@ |
|||
# More about refs |
|||
|
|||
After returning the structure of your UI from the render method, you may find yourself wanting to "reach out" and invoke methods on component instances returned from render. Often, doing something like this isn't necessary for making data flow through your application, because the Reactive data flow always ensures that the most recent `props` are sent to each child that is output from `render()`. However there are a few cases, where it still might be necessary or beneficial. |
|||
|
|||
Consider the case when you wish to tell an `<input />` element (that exists within your instances sub-hierarchy) to focus after you update its value to be the empty string, `''`. |
|||
|
|||
```javascript |
|||
var App = React.createClass({ |
|||
getInitialState: function() { |
|||
return {userInput: ''}; |
|||
}, |
|||
handleKeyUp: function(e) { |
|||
this.setState({userInput: e.target.value}); |
|||
}, |
|||
clearAndFocusInput: function() { |
|||
this.setState({userInput: ''}); // Clear the input |
|||
// We wish to focus the <input /> now! |
|||
}, |
|||
render: function() { |
|||
return ( |
|||
<div> |
|||
<div onClick={this.clearAndFocusInput}> |
|||
Click To Focus and Reset |
|||
</div> |
|||
<input |
|||
value={this.state.userInput} |
|||
onKeyUp={this.handleKeyUp} |
|||
/> |
|||
</div> |
|||
); |
|||
} |
|||
}); |
|||
``` |
|||
|
|||
|
|||
Notice how, in this example, we want to "tell" the input something - something that it cannot infer from it's props over time. In this case we want to "tell" it that it should now become focused. However, there are some challenges. What is returned from render is not your actual composition of "child" components, it is merely a *description* of the children at a particular instance in time - a snapshot, if you will. |
|||
|
|||
NOTE: Remember, what you return from `render()` is not your *actual* rendered children instances. What you return from `render()` is merely a *description* of the children instances in your component's sub-hierarchy at a particular moment in time. |
|||
|
|||
|
|||
This means that you should never "hold onto" something that you return from `render()` and then expect it to be anything meaningful. |
|||
|
|||
```javascript |
|||
// counterexample: DO NOT DO THIS! |
|||
render: function() { |
|||
var myInput = <input />; // I'm going to try to call methods on this |
|||
this.rememberThisInput = myInput; // input at some point in the future! YAY! |
|||
return ( |
|||
<div> |
|||
<div>...</div> |
|||
{myInput} |
|||
</div> |
|||
); |
|||
} |
|||
``` |
|||
|
|||
In this counterexample, the `<input />` is merely a *description* of an `<input />`. This description is used to create a *real* **backing instance** for the `<input />`. |
|||
|
|||
So how do we talk to the *real* backing instance of the input? |
|||
|
|||
## The `ref` attribute |
|||
|
|||
React supports a very special property that you can attach to any component that is output from `render()`. This special property allows you to refer to the corresponding **backing instance** of anything returned from `render()`. It is always guaranteed to be the proper instance, at any point in time. |
|||
|
|||
It's as simple as: |
|||
|
|||
1. Assign a `ref` attribute to anything returned from `render` such as: |
|||
|
|||
```html |
|||
<input ref="myInput" /> |
|||
``` |
|||
|
|||
2. In some other code (typically event handler code), access the **backing instance** via `this.refs` as in: |
|||
|
|||
```javascript |
|||
this.refs.myInput |
|||
``` |
|||
|
|||
## Completing the Example |
|||
|
|||
```javascript |
|||
var App = React.createClass({ |
|||
getInitialState: function() { |
|||
return {userInput: ''}; |
|||
}, |
|||
handleKeyUp: function(e) { |
|||
this.setState({userInput: e.target.value}); |
|||
}, |
|||
clearAndFocusInput: function() { |
|||
this.setState({userInput: ''}); // Clear the input |
|||
this.refs.theInput.getDOMNode().focus(); // Boom! Focused! |
|||
}, |
|||
render: function() { |
|||
return ( |
|||
<div> |
|||
<div onClick={this.clearAndFocusInput}> |
|||
Click To Focus and Reset |
|||
</div> |
|||
<input |
|||
ref="theInput" |
|||
value={this.state.userInput} |
|||
onKeyUp={this.handleKeyUp} |
|||
/> |
|||
</div> |
|||
); |
|||
} |
|||
}); |
|||
``` |
|||
|
|||
In this example, our render function returns a description of an <input /> instance. But the true instance is accessed via `this.refs.theInput`. As long as a child component with `ref="theInput"` is returned from render, `this.refs.theInput` will access the the proper instance. This even works on higher level (non-DOM) components such as <Typeahead ref="myTypeahead" />. |
|||
|
|||
|
|||
## Summary |
|||
|
|||
Refs are a great way to send a message to a particular child instance in a way that would be inconvenient to do via streaming Reactive `props` and `state`. They should, however, not be your go-to abstraction for flowing data through your application. By default, use the Reactive data flow and save `ref`s for use cases that are inherently non-reactive. |
|||
|
|||
**Benefits**: |
|||
|
|||
- You can define any public method on your component classes (such as a reset method on a Typeahead) and call those public methods through refs (such as this.refs.myTypeahead.reset()). |
|||
|
|||
- Performing DOM measurements almost always requires reaching out to a "native" component such as <input /> and accessing its underlying DOM node via this.refs.myInput.getDOMNode(). Refs are one of the only practical ways of doing this reliably. |
|||
Refs are automatically book-kept for you! If that child is destroyed, its ref is also destroyed for you. No worrying about memory here (unless you do something crazy to retain a reference yourself). |
|||
|
|||
**Cautions**: |
|||
|
|||
- **Never** access refs inside of any component's render method - or while any component's render method is even running anywhere in the call stack. |
|||
|
|||
- If you want to preserve Google Closure Compiler Crushing resilience, make sure to never access as a property what was specified as a string. This means you must access using `this.refs['myRefString']` if your ref was defined as `ref="myRefString"`. |
|||
|
|||
- If you have not programmed several apps with React, your first inclination is usually going to be to try to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where `state` should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. Placing the state there often eliminates any desire to use `ref`s to "make things happen" - instead, the data flow will usually accomplish your goal. |
Loading…
Reference in new issue