Paul O’Shannessy
11 years ago
18 changed files with 475 additions and 0 deletions
@ -0,0 +1,25 @@ |
|||||
|
--- |
||||
|
layout: default |
||||
|
sectionid: tips |
||||
|
--- |
||||
|
|
||||
|
<section class="content wrap documentationContent"> |
||||
|
{% include nav_docs.html %} |
||||
|
|
||||
|
<div class="inner-content"> |
||||
|
<h1>{{ page.title }}</h1> |
||||
|
<div class="subHeader">{{ page.description }}</div> |
||||
|
{{ content }} |
||||
|
|
||||
|
<div class="docs-prevnext"> |
||||
|
{% if page.prev %} |
||||
|
<a class="docs-prev" href="/react/tips/{{ page.prev }}">← Prev</a> |
||||
|
{% endif %} |
||||
|
{% if page.next %} |
||||
|
<a class="docs-next" href="/react/tips/{{ page.next }}">Next →</a> |
||||
|
{% endif %} |
||||
|
</div> |
||||
|
|
||||
|
<div class="fb-comments" data-width="650" data-num-posts="10" data-href="{{ site.url }}{{ site.baseurl }}{{ page.url }}"></div> |
||||
|
</div> |
||||
|
</section> |
@ -0,0 +1,13 @@ |
|||||
|
--- |
||||
|
id: introduction |
||||
|
title: Introduction |
||||
|
layout: tips |
||||
|
permalink: introduction.html |
||||
|
next: inline-styles.html |
||||
|
--- |
||||
|
|
||||
|
The React tips section provides bite-sized information that can answer lots of questions you might have and warn you against common pitfalls. |
||||
|
|
||||
|
## Contributing |
||||
|
|
||||
|
Submit a pull request to the [React repository](https://github.com/facebook/react) following the [current tips](https://github.com/facebook/react/tree/master/docs) entries' style. If you have a recipe that needs review prior to submitting a PR you can find help in the [#reactjs channel on freenode](irc://chat.freenode.net/reactjs) or the [reactjs Google group](http://groups.google.com/group/reactjs). Also, check the [Tips Wiki](https://github.com/facebook/react/wiki/Tips-(Previously-Cookbook)) for entries in-progress and general guidelines on writing React tips. |
@ -0,0 +1,24 @@ |
|||||
|
--- |
||||
|
id: inline-styles |
||||
|
title: Inline Styles |
||||
|
layout: tips |
||||
|
permalink: inline-styles.html |
||||
|
next: if-else-in-JSX.html |
||||
|
prev: introduction.html |
||||
|
--- |
||||
|
|
||||
|
In React, inline styles are not specified as a string. Instead they are specified with an object whose key is the camelCased version of the style name, and whose value is the style's value, usually a string ([more on that later](/react/tips/style-props-value-px.html)): |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
var divStyle = { |
||||
|
color: 'white', |
||||
|
backgroundImage: 'url(' + imgUrl + ')', |
||||
|
WebkitTransition: 'all' // note the capital 'W' here |
||||
|
}; |
||||
|
|
||||
|
React.renderComponent(<div style={divStyle}>Hello World!</div>, mountNode); |
||||
|
``` |
||||
|
|
||||
|
Style keys are camelCased in order to be consistent with accessing the properties on DOM nodes from JS (e.g. `node.style.backgroundImage`). Vendor prefixes should begin with a capital letter. This is why `WebkitTransition` has an uppercase "W". |
@ -0,0 +1,42 @@ |
|||||
|
--- |
||||
|
id: if-else-in-JSX |
||||
|
title: If-Else in JSX |
||||
|
layout: tips |
||||
|
permalink: if-else-in-JSX.html |
||||
|
prev: inline-styles.html |
||||
|
next: self-closing-tag.html |
||||
|
--- |
||||
|
|
||||
|
`if-else` statements don't work inside JSX. This is because JSX is just syntactic sugar for function calls and object construction. Take this basic example: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
// This JSX: |
||||
|
React.renderComponent(<div id="msg">Hello World!</div>, mountNode); |
||||
|
|
||||
|
// Is transformed to this JS: |
||||
|
React.renderComponent(React.DOM.div({id:"msg"}, "Hello World!"), mountNode); |
||||
|
``` |
||||
|
|
||||
|
This means that `if` statements don't fit in. Take this example: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
// This JSX: |
||||
|
<div id={if (condition) { 'msg' }}>Hello World!</div> |
||||
|
|
||||
|
// Is transformed to this JS: |
||||
|
React.DOM.div({id: if (condition) { 'msg' }}, "Hello World!"); |
||||
|
``` |
||||
|
|
||||
|
That's not valid JS. You probably want to make use of a ternary expression: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
React.renderComponent(<div id={condition ? 'msg' : ''}>Hello World!</div>, mountNode); |
||||
|
``` |
||||
|
|
||||
|
Try using it today with the [JSX compiler](/react/jsx-compiler.html). |
@ -0,0 +1,14 @@ |
|||||
|
--- |
||||
|
id: self-closing-tag |
||||
|
title: Self-Closing Tag |
||||
|
layout: tips |
||||
|
permalink: self-closing-tag.html |
||||
|
prev: if-else-in-JSX.html |
||||
|
next: maximum-number-of-jsx-root-nodes.html |
||||
|
--- |
||||
|
|
||||
|
In JSX, `<MyComponent />` alone is valid while `<MyComponent>` isn't. All tags must be closed, either with the self-closing format or with a corresponding closing tag (`</MyComponent>`). |
||||
|
|
||||
|
> Note: |
||||
|
> |
||||
|
> Every React component can be self-closing: `<div />`. `<div></div>` is also an equivalent. |
@ -0,0 +1,12 @@ |
|||||
|
--- |
||||
|
id: maximum-number-of-jsx-root-nodes |
||||
|
title: Maximum Number of JSX Root Nodes |
||||
|
layout: tips |
||||
|
permalink: maximum-number-of-jsx-root-nodes.html |
||||
|
prev: self-closing-tag.html |
||||
|
next: style-props-value-px.html |
||||
|
--- |
||||
|
|
||||
|
Currently, in a component's `render`, you can only return one node; if you have, say, a list of `div`s to return, you must wrap your components within a `div`, `span` or any other component. |
||||
|
|
||||
|
Don't forget that JSX compiles into regular js; returning two functions doesn't really make syntactic sense. Likewise, don't put more than one child in a ternary. |
@ -0,0 +1,29 @@ |
|||||
|
--- |
||||
|
id: style-props-value-px |
||||
|
title: Shorthand for Specifying Pixel Values in style props |
||||
|
layout: tips |
||||
|
permalink: style-props-value-px.html |
||||
|
prev: maximum-number-of-jsx-root-nodes.html |
||||
|
next: children-props-type.html |
||||
|
--- |
||||
|
|
||||
|
When specifying a pixel value for your inline `style` prop, React automatically appends the string "px" for you after your number value, so this works: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
var divStyle = {height: 10}; // rendered as "height:10px" |
||||
|
React.renderComponent(<div style={divStyle}>Hello World!</div>, mountNode); |
||||
|
``` |
||||
|
|
||||
|
See [Inline Styles](/react/tips/inline-styles.html) for more info. |
||||
|
|
||||
|
Sometimes you _do_ want to keep the CSS properties unitless. Here's a list of properties that won't get the automatic "px" suffix: |
||||
|
|
||||
|
- `fillOpacity` |
||||
|
- `fontWeight` |
||||
|
- `lineHeight` |
||||
|
- `opacity` |
||||
|
- `orphans` |
||||
|
- `zIndex` |
||||
|
- `zoom` |
@ -0,0 +1,49 @@ |
|||||
|
--- |
||||
|
id: children-props-type |
||||
|
title: Type of the Children props |
||||
|
layout: tips |
||||
|
permalink: children-props-type.html |
||||
|
prev: style-props-value-px.html |
||||
|
next: controlled-input-null-value.html |
||||
|
--- |
||||
|
|
||||
|
Usually, a component's children (`this.props.children`) is an array of components: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
var GenericWrapper = React.createClass({ |
||||
|
componentDidMount: function() { |
||||
|
console.log(Array.isArray(this.props.children)); // => true |
||||
|
}, |
||||
|
render: function() { |
||||
|
return <div />; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
React.renderComponent( |
||||
|
<GenericWrapper><span/><span/><span/></GenericWrapper>, |
||||
|
mountNode |
||||
|
); |
||||
|
``` |
||||
|
|
||||
|
However, when there is only a single child, `this.props.children` will be the single child component itself _without the array wrapper_. This saves an array allocation. |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
var GenericWrapper = React.createClass({ |
||||
|
componentDidMount: function() { |
||||
|
console.log(Array.isArray(this.props.children)); // => false |
||||
|
|
||||
|
// warning: yields 5 for length of the string 'hello', not 1 for the |
||||
|
// length of the non-existant array wrapper! |
||||
|
console.log(this.props.children.length); |
||||
|
}, |
||||
|
render: function() { |
||||
|
return <div />; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
React.renderComponent(<GenericWrapper>hello</GenericWrapper>, mountNode); |
||||
|
``` |
@ -0,0 +1,24 @@ |
|||||
|
--- |
||||
|
id: controlled-input-null-value |
||||
|
title: Value of null for Controlled Input |
||||
|
layout: tips |
||||
|
permalink: controlled-input-null-value.html |
||||
|
prev: children-props-type.html |
||||
|
next: componentWillReceiveProps-not-triggered-after-mounting.html |
||||
|
--- |
||||
|
|
||||
|
Specifying the `value` prop on a [controlled component](/react/docs/forms.html) prevents the user from changing the input unless you desire so. |
||||
|
|
||||
|
You might have run into a problem where `value` is specified, but the input can still be changed without consent. In this case, you might have accidentally set `value` to `undefined` or `null`. |
||||
|
|
||||
|
The snippet below shows this phenomenon; after a second, the text becomes editable. |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
React.renderComponent(<input value="hi" />, mountNode); |
||||
|
|
||||
|
setTimeout(function() { |
||||
|
React.renderComponent(<input value={null} />, mountNode); |
||||
|
}, 2000); |
||||
|
``` |
@ -0,0 +1,12 @@ |
|||||
|
--- |
||||
|
id: componentWillReceiveProps-not-triggered-after-mounting |
||||
|
title: componentWillReceiveProps Not Triggered After Mounting |
||||
|
layout: tips |
||||
|
permalink: componentWillReceiveProps-not-triggered-after-mounting.html |
||||
|
prev: controlled-input-null-value.html |
||||
|
next: props-in-getInitialState-as-anti-pattern.html |
||||
|
--- |
||||
|
|
||||
|
`componentWillReceiveProps` isn't triggered after the node is put on scene. This is by design. Check out [other lifecycle methods](/react/docs/component-specs.html) for the one that suits your needs. |
||||
|
|
||||
|
The reason for that is because `componentWillReceiveProps` often handles the logic of comparing with the old props and acting upon changes; not triggering it at mounting (where there are no old props) helps in defining what the method does. |
@ -0,0 +1,62 @@ |
|||||
|
--- |
||||
|
id: props-in-getInitialState-as-anti-pattern |
||||
|
title: Props in getInitialState Is an Anti-Pattern |
||||
|
layout: tips |
||||
|
permalink: props-in-getInitialState-as-anti-pattern.html |
||||
|
prev: componentWillReceiveProps-not-triggered-after-mounting.html |
||||
|
next: dom-event-listeners.html |
||||
|
--- |
||||
|
|
||||
|
> Note: |
||||
|
> |
||||
|
> This isn't really a React-specific tip, as such anti-patterns often occur in code in general; in this case, React simply points them out more clearly. |
||||
|
|
||||
|
Using props, passed down from parent, to generate state in `getInitialState` often leads to duplication of "source of truth", i.e. where the real data is. Whenever possible, compute values on-the-fly to ensure that they don't get out of sync later on and cause maintenance trouble. |
||||
|
|
||||
|
Bad example: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
var MessageBox = React.createClass({ |
||||
|
getInitialState: function() { |
||||
|
return {nameWithQualifier: "Mr. " + this.props.name}; |
||||
|
}, |
||||
|
render: function() { |
||||
|
return <div>{this.state.nameWithQualifier}</div>; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
React.renderComponent(<MessageBox name="Rogers"/>, mountNode); |
||||
|
``` |
||||
|
|
||||
|
Better: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
var MessageBox = React.createClass({ |
||||
|
render: function() { |
||||
|
return <div>{"Mr. " + this.props.name}</div>; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
React.renderComponent(<MessageBox name="Rogers"/>, mountNode); |
||||
|
``` |
||||
|
|
||||
|
For more complex logic: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
var MessageBox = React.createClass({ |
||||
|
render: function() { |
||||
|
return <div>{this.getNameWithQualifier(this.props.name)}</div>; |
||||
|
}, |
||||
|
getNameWithQualifier: function(name) { |
||||
|
return 'Mr. ' + name; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
React.renderComponent(<MessageBox name="Rogers"/>, mountNode); |
||||
|
``` |
@ -0,0 +1,44 @@ |
|||||
|
--- |
||||
|
id: dom-event-listeners |
||||
|
title: DOM Event Listeners in a Component |
||||
|
layout: tips |
||||
|
permalink: dom-event-listeners.html |
||||
|
prev: props-in-getInitialState-as-anti-pattern.html |
||||
|
next: initial-ajax.html |
||||
|
--- |
||||
|
|
||||
|
> Note: |
||||
|
> |
||||
|
> This entry shows how to attach DOM events not provided by React ([check here for more info](/react/docs/events.html)). This is good for integrations with other libraries such as jQuery. |
||||
|
|
||||
|
Try to resize the window: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
var Box = React.createClass({ |
||||
|
getInitialState: function() { |
||||
|
return {windowWidth: window.innerWidth}; |
||||
|
}, |
||||
|
|
||||
|
handleResize: function(e) { |
||||
|
this.setState({windowWidth: window.innerWidth}); |
||||
|
}, |
||||
|
|
||||
|
componentDidMount: function() { |
||||
|
window.addEventListener('resize', this.handleResize); |
||||
|
}, |
||||
|
|
||||
|
componentWillUnmount: function() { |
||||
|
window.removeEventListener('resize', this.handleResize); |
||||
|
}, |
||||
|
|
||||
|
render: function() { |
||||
|
return <div>Current window width: {this.state.windowWidth}</div>; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
React.renderComponent(<Box />, mountNode); |
||||
|
``` |
||||
|
|
||||
|
`componentDidMount` is called after the component is mounted and has a DOM representation. This is often a place where you would attach generic DOM events. |
@ -0,0 +1,47 @@ |
|||||
|
--- |
||||
|
id: initial-ajax |
||||
|
title: Load Initial Data via AJAX |
||||
|
layout: tips |
||||
|
permalink: initial-ajax.html |
||||
|
prev: dom-event-listeners.html |
||||
|
next: false-in-jsx.html |
||||
|
--- |
||||
|
|
||||
|
Fetch data in `componentDidMount`. When the response arrives, store the data in state, triggering a render to update your UI. |
||||
|
|
||||
|
This example fetches the desired Github user's lastest gist: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
|
||||
|
var UserGist = React.createClass({ |
||||
|
getInitialState: function() { |
||||
|
return { |
||||
|
username: '', |
||||
|
lastGistUrl: '' |
||||
|
}; |
||||
|
}, |
||||
|
componentDidMount: function() { |
||||
|
$.get(this.props.source, function(result) { |
||||
|
var lastGist = result[0]; |
||||
|
this.setState({ |
||||
|
username: lastGist.user.login, |
||||
|
lastGistUrl: lastGist.html_url |
||||
|
}); |
||||
|
}.bind(this)); |
||||
|
}, |
||||
|
render: function() { |
||||
|
return ( |
||||
|
<div> |
||||
|
{this.state.username}'s last gist is |
||||
|
<a href={this.state.lastGistUrl}>here</a>. |
||||
|
</div> |
||||
|
); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
React.renderComponent( |
||||
|
<UserGist source="https://api.github.com/users/octocat/gists" />, |
||||
|
mountNode |
||||
|
); |
||||
|
``` |
@ -0,0 +1,32 @@ |
|||||
|
--- |
||||
|
id: false-in-jsx |
||||
|
title: False in JSX |
||||
|
layout: tips |
||||
|
permalink: false-in-jsx.html |
||||
|
prev: initial-ajax.html |
||||
|
--- |
||||
|
|
||||
|
Here's how `false` renders in different contexts: |
||||
|
|
||||
|
Renders as `id="false"`: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
React.renderComponent(<div id={false} />, mountNode); |
||||
|
``` |
||||
|
|
||||
|
String "false" as input value: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
React.renderComponent(<input value={false} />, mountNode); |
||||
|
``` |
||||
|
|
||||
|
No child: |
||||
|
|
||||
|
```js |
||||
|
/** @jsx React.DOM */ |
||||
|
React.renderComponent(<div>{false}</div>, mountNode); |
||||
|
``` |
||||
|
|
||||
|
The reason why this one doesn't render as the string `"false"` as a `div` child is to allow the more common use-case: `<div>{x > 1 && 'You have more than one item'}</div>`. |
Loading…
Reference in new issue