Browse Source

Merge pull request #362 from mcsheffrey/feat-documentation-cookbook

React Tips documentation
main
Paul O’Shannessy 11 years ago
parent
commit
0565c94864
  1. 1
      README.md
  2. 29
      _config.yml
  3. 15
      _includes/nav_docs.html
  4. 1
      _layouts/default.html
  5. 25
      _layouts/tips.html
  6. 13
      tips/01-introduction.md
  7. 24
      tips/02-inline-styles.md
  8. 42
      tips/03-if-else-in-JSX.md
  9. 14
      tips/04-self-closing-tag.md
  10. 12
      tips/05-maximum-number-of-jsx-root-nodes.md
  11. 29
      tips/06-style-props-value-px.md
  12. 49
      tips/07-children-props-type.md
  13. 24
      tips/08-controlled-input-null-value.md
  14. 12
      tips/09-componentWillReceiveProps-not-triggered-after-mounting.md
  15. 62
      tips/10-props-in-getInitialState-as-anti-pattern.md
  16. 44
      tips/11-dom-event-listeners.md
  17. 47
      tips/12-initial-ajax.md
  18. 32
      tips/13-false-in-jsx.md

1
README.md

@ -21,6 +21,7 @@ Once you have RubyGems and installed Bundler (via `gem install bundler`), use it
```sh
$ cd react/docs
$ bundle install # Might need sudo.
$ npm install # Might need sudo.
```
### Instructions

29
_config.yml

@ -69,3 +69,32 @@ nav_docs_sections:
title: Event System
- id: dom-differences
title: DOM Differences
nav_tips:
- title: Tips
items:
- id: introduction
title: Introduction
- id: inline-styles
title: Inline Styles
- id: if-else-in-JSX
title: If-Else in JSX
- id: self-closing-tag
title: Self-Closing Tag
- id: maximum-number-of-jsx-root-nodes
title: Maximum Number of JSX Root Nodes
- id: style-props-value-px
title: Shorthand for Specifying Pixel Values in style props
- id: children-props-type
title: Type of the Children props
- id: controlled-input-null-value
title: Value of null for Controlled Input
- id: componentWillReceiveProps-not-triggered-after-mounting
title: componentWillReceiveProps Not Triggered After Mounting
- id: props-in-getInitialState-as-anti-pattern
title: Props in getInitialState Is an Anti-Pattern
- id: dom-event-listeners
title: DOM Event Listeners in a Component
- id: initial-ajax
title: Load Initial Data via AJAX
- id: false-in-jsx
title: False in JSX

15
_includes/nav_docs.html

@ -1,4 +1,5 @@
<div class="nav-docs">
<!-- Docs Nav -->
{% for section in site.nav_docs_sections %}
<div class="nav-docs-section">
<h3>{{ section.title }}</h3>
@ -24,4 +25,18 @@
</ul>
</div>
{% endfor %}
<!-- Tips Nav -->
{% for section in site.nav_tips %}
<div class="nav-docs-section">
<h3>{{ section.title }}</h3>
<ul>
{% for item in section.items %}
<li>
<a href="/react/tips/{{ item.id }}.html"{% if page.id == item.id %} class="active"{% endif %}>{{ item.title }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</div>

1
_layouts/default.html

@ -74,6 +74,7 @@
</footer>
</div>
<div id="fb-root"></div>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),

25
_layouts/tips.html

@ -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 }}">&larr; Prev</a>
{% endif %}
{% if page.next %}
<a class="docs-next" href="/react/tips/{{ page.next }}">Next &rarr;</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>

13
tips/01-introduction.md

@ -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.

24
tips/02-inline-styles.md

@ -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".

42
tips/03-if-else-in-JSX.md

@ -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).

14
tips/04-self-closing-tag.md

@ -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.

12
tips/05-maximum-number-of-jsx-root-nodes.md

@ -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.

29
tips/06-style-props-value-px.md

@ -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`

49
tips/07-children-props-type.md

@ -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);
```

24
tips/08-controlled-input-null-value.md

@ -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);
```

12
tips/09-componentWillReceiveProps-not-triggered-after-mounting.md

@ -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.

62
tips/10-props-in-getInitialState-as-anti-pattern.md

@ -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);
```

44
tips/11-dom-event-listeners.md

@ -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.

47
tips/12-initial-ajax.md

@ -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
);
```

32
tips/13-false-in-jsx.md

@ -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…
Cancel
Save