This is a note for learn React Advanced guides, you can learn it in facebook.github.io/react.
JSX In Depth
Props Default to "True"
If you pass no value for a prop, it defaults to true. These two JSX expressions are equivalent:
<MyTextBox autocomplete />
//is equal
<MyTextBox autocomplete={true} />
<MyTextBox active/>
console.log(this.props.active)
// true
<MyTextBox />
console.log(this.props.active)
// undefined
Spread Attributes
Spread attributes can be useful when you are building generic containers. However, they can also make your code messy by making it easy to pass a lot of irrelevant props to components that don't care about them. We recommend that you use this syntax sparingly.
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}
String Literals
JSX removes whitespace at the beginning and ending of a line. It also removes blank lines. New lines adjacent to tags are removed; new lines that occur in the middle of string literals are condensed into a single space. So these all render to the same thing:
<div>Hello World</div>
<div>
Hello World
</div>
<div>
Hello
World
</div>
<div>
Hello World
</div>
Booleans, Null, and Undefined Are Ignored
false, null, undefined, and true are valid children. They simply don't render. These JSX expressions will all render to the same thing:
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{true}</div>
One caveat is that some "falsy" values, such as the 0 number, are still rendered by React. For example, this code will not behave as you might expect because 0 will be printed when props.messages is an empty array:
<div>
{props.messages.length &&
<MessageList messages={props.messages} />
}
</div>
To fix this, make sure that the expression before && is always boolean:
<div>
{props.messages.length > 0 &&
<MessageList messages={props.messages} />
}
</div>
Conversely, if you want a value like false, true, null, or undefined to appear in the output, you have to convert it to a string first:
<div>
My JavaScript variable is {String(myVariable)}.
</div>
Typechecking With PropTypes
Regular PropTypes:
MyComponent.propTypes = {
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
optionalSymbol: React.PropTypes.symbol,
}
You can ensure that your prop is limited to specific values by treating it as an enum:
optionalEnum: React.PropTypes.oneOf(['News', 'Photos'])
An object that could be one of many types:
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
])
An array of a certain type:
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number)
An object with property values of a certain type:
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number)
An object taking on a particular shape:
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
})
You can also specify a custom validator. It should return an Error object if the validation fails. Don't console.warn
or throw, as this won't work inside oneOfType
:
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
}
You can also supply a custom validator to arrayOf
and objectOf
.It should return an Error object if the validation fails. The validator will be called for each key in the array or object. The first two arguments of the validator are the array or object itself, and the current item's key.
customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
Default Prop Values
The propTypes typechecking happens after defaultProps are resolved, so typechecking will also apply to the defaultProps:
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
)
}
}
// Specifies the default values for props:
Greeting.defaultProps = {
name: 'Stranger'
}
Refs and the DOM
The ref Callback Attribute
The ref attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted.
When the ref attribute is used on an HTML element, the ref callback receives the underlying DOM element as its argument. For example, this code uses the ref callback to store a reference to a DOM node:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.handleFocus = this.handleFocus.bind(this);
}
handleFocus() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in this.textInput.
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.handleFocus}
/>
</div>
);
}
}
React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts.
When the ref attribute is used on a custom component, the ref callback receives the mounted instance of the component as its argument. For example, if we wanted to wrap the CustomTextInput above to simulate it being clicked immediately after mounting:
class AutoFocusTextInput extends React.Component {
componentDidMount() {
this.customTextInput.handleFocus();
}
render() {
return (
<CustomTextInput
ref={(customTextInput) => { this.customTextInput = customTextInput; }} />
);
}
}
Functional components
TextInput must be declared here so the ref callback can refer to it
function CustomTextInput(props) {
// textInput must be declared here so the ref callback can refer to it
let textInput = null;
function handleClick() {
textInput.focus();
}
return (
<div>
<input
type="text"
ref={(input) => { textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
Don't Overuse Refs
Your first inclination may be 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.
Reconciliation
The Diffing Algorithm
Elements Of Different Types
Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch.
Any components below the root will also get unmounted and have their state destroyed. For example, when diffing:
<div>
<Counter />
</div>
<span>
<Counter />
</span>
This will destroy the old Counter and remount a new one.
DOM Elements Of The Same Type
When comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes. For example:
<div className="before" title="stuff" />
<div className="after" title="stuff" />
When converting between these two elements, React knows to only modify the color style, not the fontWeight.
<div style={{color: 'red', fontWeight: 'bold'}} />
<div style={{color: 'green', fontWeight: 'bold'}} />
Recursing On Children
When adding an element at the end of the children, converting between these two trees works well:
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
React will mutate every child instead of realizing it can keep the <li>Duke</li> and <li>Villanova</li> subtrees intact. This inefficiency can be a problem.
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
Keys
Adding a key to our inefficient example above can make the tree conversion efficient:
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
The key only has to be unique among its siblings, not globally unique.
As a last resort, you can pass item's index in the array as a key. This can work well if the items are never reordered, but reorders will be slow.
Context
Why Not To Use Context
If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.
How To Use Context
Suppose you have a structure like:
class Button extends React.Component {
render() {
return (
<button style={{background: this.props.color}}>
{this.props.children}
</button>
);
}
}
class Message extends React.Component {
render() {
return (
<div>
{this.props.text} <Button color={this.props.color}>Delete</Button>
</div>
);
}
}
class MessageList extends React.Component {
render() {
const color = "purple";
const children = this.props.messages.map((message) =>
<Message text={message.text} color={color} />
);
return <div>{children}</div>;
}
}
Using context, we can pass this through the tree automatically:
class Button extends React.Component {
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}
Button.contextTypes = {
color: React.PropTypes.string
};
class Message extends React.Component {
render() {
return (
<div>
{this.props.text} <Button>Delete</Button>
</div>
);
}
}
class MessageList extends React.Component {
getChildContext() {
return {color: "purple"};
}
render() {
const children = this.props.messages.map((message) =>
<Message text={message.text} />
);
return <div>{children}</div>;
}
}
MessageList.childContextTypes = {
color: React.PropTypes.string
};
By adding childContextTypes and getChildContext to MessageList (the context provider), React passes the information down automatically and any component in the subtree (in this case, Button) can access it by defining contextTypes.
If contextTypes is not defined, then context will be an empty object.