Browse Source

Merge pull request #587 from bvaughn/16.3-release-blog-post

16.3 release blog draft
main
Brian Vaughn 7 years ago
committed by GitHub
parent
commit
b618684fe2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 98
      content/blog/2018-03-29-react-v-16-3.md
  2. 264
      content/docs/context.md
  3. 22
      content/docs/forwarding-refs.md
  4. 28
      content/docs/higher-order-components.md
  5. 217
      content/docs/legacy-context.md
  6. 4
      content/docs/nav.yml
  7. 89
      content/docs/reference-react-component.md
  8. 11
      content/docs/reference-react.md
  9. 207
      content/docs/refs-and-the-dom.md
  10. 4
      content/docs/state-and-lifecycle.md
  11. 88
      content/docs/strict-mode.md
  12. BIN
      content/images/blog/strict-mode-unsafe-lifecycles-warning.png
  13. 26
      examples/16-3-release-blog-post/context-example.js
  14. 14
      examples/16-3-release-blog-post/create-ref-example.js
  15. 32
      examples/16-3-release-blog-post/fancy-button-example.js
  16. 36
      examples/16-3-release-blog-post/forward-ref-example.js
  17. 10
      examples/16-3-release-blog-post/hoc-theme-example.js
  18. 11
      examples/context/forwarding-refs-app.js
  19. 16
      examples/context/forwarding-refs-fancy-button.js
  20. 10
      examples/context/higher-order-component-before.js
  21. 7
      examples/context/higher-order-component-usage.js
  22. 18
      examples/context/higher-order-component.js
  23. 28
      examples/context/lifecycles.js
  24. 23
      examples/context/motivation-problem.js
  25. 33
      examples/context/motivation-solution.js
  26. 39
      examples/context/multiple-contexts.js
  27. 10
      examples/context/reference-caveats-problem.js
  28. 17
      examples/context/reference-caveats-solution.js
  29. 48
      examples/context/theme-detailed-app.js
  30. 15
      examples/context/theme-detailed-theme-context.js
  31. 17
      examples/context/theme-detailed-themed-button.js
  32. 15
      examples/forwarding-refs/fancy-button-ref.js
  33. 12
      examples/forwarding-refs/fancy-button.js
  34. 34
      examples/forwarding-refs/log-props-after.js
  35. 16
      examples/forwarding-refs/log-props-before.js
  36. 27
      examples/react-component-reference/get-snapshot-before-update.js
  37. 28
      examples/reference-react-forward-ref.js
  38. 18
      examples/strict-mode/enabling-strict-mode.js
  39. 7
      examples/strict-mode/side-effects-in-constructor.js

98
content/blog/2018-03-29-react-v-16-3.md

@ -0,0 +1,98 @@
---
title: "React v16.3.0: New lifecycles and context API"
author: [bvaughn]
---
This release includes an official context API, new class component lifecycles, a new `StrictMode` component, a new ergonomic ref API, and a ref-forwarding API!
For the past few months, the React team has been working on support for [asynchronous rendering](/blog/2018/03/01/sneak-peek-beyond-react-16.html). We are excited about the new features it will enable.
We've also learned that some long-term changes will be required to the way we write React components. (If you haven't already, you may want to check out our [recent blog post](/blog/2018/03/27/update-on-async-rendering.html) about this.) Rest assured that we respect [semver](https://semver.org/) and **will not ship breaking changes in a minor version**!
Read on to learn more about the release.
## Official Context API
For many years, React has offered an experimental API for context. Although it was a powerful tool, its use was discouraged because of inherent problems in the API, and because we always intended to replace the experimental API with a better one.
Version 16.3 introduces a new context API that is more efficient and supports both static type checking and deep updates.
> **Note**
>
> The old context API will keep working for all React 16.x releases, so you will have time to migrate.
Here is an example illustrating how you might inject a "theme" using the new context API:
`embed:16-3-release-blog-post/context-example.js`
[Learn more about the new context API here.](/docs/context.html)
## `createRef` API
Previously, React provided two ways of managing refs: the legacy string ref API and the callback API. Although the string ref API was the more convenient of the two, it had [several downsides](https://github.com/facebook/react/issues/1373) and so our official recommendation was to use the callback form instead.
Version 16.3 adds a new option for managing refs that offers the convenience of a string ref without any of the downsides:
`embed:16-3-release-blog-post/create-ref-example.js`
> **Note:**
>
> Callback refs will continue to be supported in addition to the new `createRef` API.
>
> You don't need to replace callback refs in your components. They are slightly more flexible, so they will remain as an advanced feature.
[Learn more about the new `createRef` API here.](/docs/refs-and-the-dom.html)
## `forwardRef` API
[Higher-order components](/docs/higher-order-components.html) (or HOCs) are a common way to reuse code between components. Building on the theme context example from above, we might create an HOC that injects the current "theme" as a prop:
`embed:16-3-release-blog-post/hoc-theme-example.js`
We can use the above HOC to wire components up to the theme context without having to use `ThemeContext` directly. For example:
`embed:16-3-release-blog-post/fancy-button-example.js`
HOCs typically [pass props through](/docs/higher-order-components.html#convention-pass-unrelated-props-through-to-the-wrapped-component) to the components they wrap. Unfortunately, [refs are not passed through](/docs/higher-order-components.html#refs-arent-passed-through). This means that we can't attach a ref to `FancyButton` if we use `FancyThemedButton`- so there's no way for us to call `focus()`.
The new `forwardRef` API solves this problem by providing a way for us to intercept a `ref` and forward it as a normal prop:
`embed:16-3-release-blog-post/forward-ref-example.js`
## Component Lifecycle Changes
React's class component API has been around for years with little change. However, as we add support for more advanced features (such as [error boundaries](/docs/react-component.html#componentdidcatch) and the upcoming [async rendering mode](/blog/2018/03/01/sneak-peek-beyond-react-16.html)) we stretch this model in ways that it was not originally intended.
For example, with the current API, it is too easy to block the initial render with non-essential logic. In part this is because there are too many ways to accomplish a given task, and it can be unclear which is best. We've observed that the interrupting behavior of error handling is often not taken into consideration and can result in memory leaks (something that will also impact the upcoming async rendering mode). The current class component API also complicates other efforts, like our work on [prototyping a React compiler](https://twitter.com/trueadm/status/944908776896978946).
Many of these issues are exacerbated by a subset of the component lifecycles (`componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`). These also happen to be the lifecycles that cause the most confusion within the React community. For these reasons, we are going to deprecate those methods in favor of better alternatives.
We recognize that this change will impact many existing components. Because of this, the migration path will be as gradual as possible, and will provide escape hatches. (At Facebook, we maintain more than 50,000 React components. We depend on a gradual release cycle too!)
> **Note:**
>
> Deprecation warnings will be enabled with a future 16.x release, **but the legacy lifecycles will continue to work until version 17**.
>
> Even in version 17, it will still be possible to use them, but they will be aliased with an "UNSAFE_" prefix to indicate that they might cause issues. We have also prepared an [automated script to rename them](https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles) in existing code.
In addition to deprecating unsafe lifecycles, we are also adding a couple of new lifecyles:
* [`getDerivedStateFromProps`](/docs/react-component.html#static-getderivedstatefromprops) is being added as a safer alternative to the legacy `componentWillReceiveProps`.
* [`getSnapshotBeforeUpdate`](/docs/react-component.html#getsnapshotbeforeupdate) is being added to support safely reading properties from e.g. the DOM before updates are made.
[Learn more about these lifecycle changes here.](/blog/2018/03/27/update-on-async-rendering.html)
## `StrictMode` Component
`StrictMode` is a tool for highlighting potential problems in an application. Like `Fragment`, `StrictMode` does not render any visible UI. It activates additional checks and warnings for its descendants.
> **Note:**
>
> `StrictMode` checks are run in development mode only; _they do not impact the production build_.
Although it is not possible for strict mode to catch all problems (e.g. certain types of mutation), it can help with many. If you see warnings in strict mode, those things will likely cause bugs for async rendering.
In version 16.3, `StrictMode` helps with:
* Identifying components with unsafe lifecycles
* Warning about legacy string ref API usage
* Detecting unexpected side effects
Additional functionality will be added with future releases of React.
[Learn more about the `StrictMode` component here.](/docs/strict-mode.html)

264
content/docs/context.md

@ -4,220 +4,148 @@ title: Context
permalink: docs/context.html
---
> Note:
>
> `React.PropTypes` has moved into a different package since React v15.5. Please use [the `prop-types` library instead](https://www.npmjs.com/package/prop-types) to define `contextTypes`.
>
>We provide [a codemod script](/blog/2017/04/07/react-v15.5.0.html#migrating-from-react.proptypes) to automate the conversion.
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
With React, it's easy to track the flow of data through your React components. When you look at a component, you can see which props are being passed, which makes your apps easy to reason about.
In a typical React application, data is passed top-down (parent to child) via props, but this can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like this between components without having to explicitly pass a prop through every level of the tree.
In some cases, you want to pass data through the component tree without having to pass the props down manually at every level.
You can do this directly in React with the powerful "context" API.
- [When to Use Context](#when-to-use-context)
- [API](#api)
- [React.createContext](#reactcreatecontext)
- [Provider](#provider)
- [Consumer](#consumer)
- [Examples](#examples)
- [Dynamic Context](#dynamic-context)
- [Consuming Multiple Contexts](#consuming-multiple-contexts)
- [Accessing Context in Lifecycle Methods](#accessing-context-in-lifecycle-methods)
- [Consuming Context with a HOC](#consuming-context-with-a-hoc)
- [Forwarding Refs to Context Consumers](#forwarding-refs-to-context-consumers)
- [Caveats](#caveats)
- [Legacy API](#legacy-api)
> Note:
>
> A [new, safe version of context](https://github.com/reactjs/rfcs/blob/master/text/0002-new-version-of-context.md) is under development for the upcoming 16.3 release.
## When to Use Context
## Why Not To Use Context
Context is designed to share data that can be considered "global" for a tree of React components, such as the current authenticated user, theme, or preferred language. For example, in the code below we manually thread through a "theme" prop in order to style the Button component:
The vast majority of applications do not need to use context.
`embed:context/motivation-problem.js`
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.
Using context, we can avoid passing props through intermediate elements:
If you aren't familiar with state management libraries like [Redux](https://github.com/reactjs/redux) or [MobX](https://github.com/mobxjs/mobx), don't use context. For many practical applications, these libraries and their React bindings are a good choice for managing state that is relevant to many components. It is far more likely that Redux is the right solution to your problem than that context is the right solution.
`embed:context/motivation-solution.js`
> Note
>
> Don't use context just to avoid passing props a few levels down. Stick to cases where the same data needs to accessed in many components at multiple levels.
If you're still learning React, don't use context. There is usually a better way to implement functionality just using props and state.
## API
If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes.
### `React.createContext`
## How To Use Context
```js
const {Provider, Consumer} = React.createContext(defaultValue);
```
Suppose you have a structure like:
Creates a `{ Provider, Consumer }` pair.
```javascript
class Button extends React.Component {
render() {
return (
<button style={{background: this.props.color}}>
{this.props.children}
</button>
);
}
}
Optionally accepts a default value to be passed to Consumers without a Provider ancestor.
class Message extends React.Component {
render() {
return (
<div>
{this.props.text} <Button color={this.props.color}>Delete</Button>
</div>
);
}
}
### `Provider`
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>;
}
}
```js
<Provider value={/* some value */}>
```
In this example, we manually thread through a `color` prop in order to style the `Button` and `Message` components appropriately. Using context, we can pass this through the tree automatically:
```javascript{6,13-15,21,28-30,40-42}
import PropTypes from 'prop-types';
class Button extends React.Component {
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}
Button.contextTypes = {
color: 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: PropTypes.string
};
A React component that allows Consumers to subscribe to context changes.
Accepts a `value` prop to be passed to Consumers that are descendants of this Provider. One Provider can be connected to many Consumers. Providers can be nested to override values deeper within the tree.
### `Consumer`
```js
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
```
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`.
A React component that subscribes to context changes.
If `contextTypes` is not defined, then `context` will be an empty object.
Requires a [function as a child](/docs/render-props.html#using-props-other-than-render). The function receives the current context value and returns a React node. All consumers are re-rendered whenever the Provider value changes. Changes are determined by comparing the new and old values using the same algorithm as [`Object.is`](developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description). (This can cause some issues when passing objects as `value`: see [Caveats](#caveats).)
## Parent-Child Coupling
> Note
>
> For more information about this pattern, see [render props](/docs/render-props.html).
Context can also let you build an API where parents and children communicate. For example, one library that works this way is [React Router V4](https://reacttraining.com/react-router):
## Examples
```javascript
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
### Dynamic Context
const BasicExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
A more complex example with dynamic values for the theme:
<hr />
**theme-context.js**
`embed:context/theme-detailed-theme-context.js`
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>
);
```
**themed-button.js**
`embed:context/theme-detailed-themed-button.js`
By passing down some information from the `Router` component, each `Link` and `Route` can communicate back to the containing `Router`.
**app.js**
`embed:context/theme-detailed-app.js`
Before you build components with an API similar to this, consider if there are cleaner alternatives. For example, you can pass entire React components as props if you'd like to.
### Consuming Multiple Contexts
## Referencing Context in Lifecycle Methods
To keep context re-rendering fast, React needs to make each context consumer a separate node in the tree.
If `contextTypes` is defined within a component, the following [lifecycle methods](/docs/react-component.html#the-component-lifecycle) will receive an additional parameter, the `context` object:
`embed:context/multiple-contexts.js`
- [`constructor(props, context)`](/docs/react-component.html#constructor)
- [`componentWillReceiveProps(nextProps, nextContext)`](/docs/react-component.html#componentwillreceiveprops)
- [`shouldComponentUpdate(nextProps, nextState, nextContext)`](/docs/react-component.html#shouldcomponentupdate)
- [`componentWillUpdate(nextProps, nextState, nextContext)`](/docs/react-component.html#componentwillupdate)
If two or more context values are often used together, you might want to consider creating your own render prop component that provides both.
> Note:
>
> As of React 16, `componentDidUpdate` no longer receives `prevContext`.
### Accessing Context in Lifecycle Methods
## Referencing Context in Stateless Functional Components
Accessing values from context in lifecycle methods is a relatively common use case. Instead of adding context to every lifecycle method, you just need to pass it as a prop, and then work with it just like you'd normally work with a prop.
Stateless functional components are also able to reference `context` if `contextTypes` is defined as a property of the function. The following code shows a `Button` component written as a stateless functional component.
`embed:context/lifecycles.js`
```javascript
import PropTypes from 'prop-types';
### Consuming Context with a HOC
const Button = ({children}, context) =>
<button style={{background: context.color}}>
{children}
</button>;
Some types of contexts are consumed by many components (e.g. theme or localization). It can be tedious to explicitly wrap each dependency with a `<Context.Consumer>` element. A [higher-order component](/docs/higher-order-components.html) can help with this.
Button.contextTypes = {color: PropTypes.string};
```
For example, a button component might consume a theme context like so:
## Updating Context
`embed:context/higher-order-component-before.js`
Don't do it.
That's alright for a few components, but what if we wanted to use the theme context in a lot of places?
React has an API to update context, but it is fundamentally broken and you should not use it.
We could create a higher-order component called `withTheme`:
The `getChildContext` function will be called when the state or props changes. In order to update data in the context, trigger a local state update with `this.setState`. This will trigger a new context and changes will be received by the children.
`embed:context/higher-order-component.js`
```javascript
import PropTypes from 'prop-types';
Now any component that depends on the theme context can easy subscribe to it using the `withTheme` function we've created:
class MediaQuery extends React.Component {
constructor(props) {
super(props);
this.state = {type:'desktop'};
}
`embed:context/higher-order-component-usage.js`
getChildContext() {
return {type: this.state.type};
}
### Forwarding Refs to Context Consumers
componentDidMount() {
const checkMediaQuery = () => {
const type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
if (type !== this.state.type) {
this.setState({type});
}
};
One issue with the render prop API is that refs don't automatically get passed to wrapped elements. To get around this, use `React.forwardRef`:
window.addEventListener('resize', checkMediaQuery);
checkMediaQuery();
}
**fancy-button.js**
`embed:context/forwarding-refs-fancy-button.js`
render() {
return this.props.children;
}
}
**app.js**
`embed:context/forwarding-refs-app.js`
MediaQuery.childContextTypes = {
type: PropTypes.string
};
```
## Caveats
Because context uses reference identity to determine when to re-render, there are some gotchas that could trigger unintentional renders in consumers when a provider's parent re-renders. For example, the code below will re-render all consumers every time the Provider re-renders because a new object is always created for `value`:
`embed:context/reference-caveats-problem.js`
To get around this, lift the value into the parent's state:
`embed:context/reference-caveats-solution.js`
## Legacy API
The problem is, if a context value provided by component changes, descendants that use that value won't update if an intermediate parent returns `false` from `shouldComponentUpdate`. This is totally out of control of the components using context, so there's basically no way to reliably update the context. [This blog post](https://medium.com/@mweststrate/how-to-safely-use-react-context-b7e343eff076) has a good explanation of why this is a problem and how you might get around it.
> Note
>
> React previously shipped with an experimental context API. The old API will be supported in all 16.x releases, but applications using it should migrate to the new version. The legacy API will be removed in a future major React version. Read the [legacy context docs here](/docs/legacy-context.html).

22
content/docs/forwarding-refs.md

@ -0,0 +1,22 @@
---
id: forwarding-refs
title: Forwarding Refs
permalink: docs/forwarding-refs.html
---
Ref forwarding is a technique for passing a [ref](/docs/refs-and-the-dom.html) through a component to one of its descendants. This technique can be particularly useful with [higher-order components](/docs/higher-order-components.html) (also known as HOCs).
Let's start with an example HOC that logs component props to the console:
`embed:forwarding-refs/log-props-before.js`
The "logProps" HOC passes all `props` through to the component it wraps, so the rendered output will be the same. For example, we can use this HOC to log all props that get passed to our "fancy button" component:
`embed:forwarding-refs/fancy-button.js`
There is one caveat to the above example: refs will not get passed through. That's because `ref` is not a prop. Like `key`, it's handled differently by React. If you add a ref to a HOC, the ref will refer to the outermost container component, not the wrapped component.
This means that refs intended for our `FancyButton` component will actually be attached to the `LogProps` component:
`embed:forwarding-refs/fancy-button-ref.js`
Fortunately, we can explicitly forward refs to the inner `FancyButton` component using the `React.forwardRef` API. `React.forwardRef` accepts a render function that receives `props` and `ref` parameters and returns a React node. For example:
`embed:forwarding-refs/log-props-after.js`

28
content/docs/higher-order-components.md

@ -394,30 +394,6 @@ import MyComponent, { someFunction } from './MyComponent.js';
### Refs Aren't Passed Through
While the convention for higher-order components is to pass through all props to the wrapped component, it's not possible to pass through refs. That's because `ref` is not really a prop — like `key`, it's handled specially by React. If you add a ref to an element whose component is the result of a HOC, the ref refers to an instance of the outermost container component, not the wrapped component.
While the convention for higher-order components is to pass through all props to the wrapped component, this does not work for refs. That's because `ref` is not really a prop — like `key`, it's handled specially by React. If you add a ref to an element whose component is the result of a HOC, the ref refers to an instance of the outermost container component, not the wrapped component.
If you find yourself facing this problem, the ideal solution is to figure out how to avoid using `ref` at all. Occasionally, users who are new to the React paradigm rely on refs in situations where a prop would work better.
That said, there are times when refs are a necessary escape hatch — React wouldn't support them otherwise. Focusing an input field is an example where you may want imperative control of a component. In that case, one solution is to pass a ref callback as a normal prop, by giving it a different name:
```js
function Field({ inputRef, ...rest }) {
return <input ref={inputRef} {...rest} />;
}
// Wrap Field in a higher-order component
const EnhancedField = enhance(Field);
// Inside a class component's render method...
<EnhancedField
inputRef={(inputEl) => {
// This callback gets passed through as a regular prop
this.inputEl = inputEl
}}
/>
// Now you can call imperative methods
this.inputEl.focus();
```
This is not a perfect solution by any means. We prefer that refs remain a library concern, rather than require you to manually handle them. We are exploring ways to solve this problem so that using a HOC is unobservable.
The solution for this problem is to use the `React.forwardRef` API (introduced with React 16.3). [Learn more about it in the forwarding refs section](/docs/forwarding-refs.html).

217
content/docs/legacy-context.md

@ -0,0 +1,217 @@
---
id: legacy-context
title: Legacy Context
permalink: docs/legacy-context.html
---
> Note:
>
> The legacy context API will be removed in a future major version.
> Use the [new context API](/docs/context.html) introduced with version 16.3.
> The legacy API will continue working for all 16.x releases.
## How To Use Context
> This section documents a legacy API. See the [new API](/docs/context.html).
Suppose you have a structure like:
```javascript
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>;
}
}
```
In this example, we manually thread through a `color` prop in order to style the `Button` and `Message` components appropriately. Using context, we can pass this through the tree automatically:
```javascript{6,13-15,21,28-30,40-42}
import PropTypes from 'prop-types';
class Button extends React.Component {
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}
Button.contextTypes = {
color: 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: 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.
> Note:
>
> `React.PropTypes` has moved into a different package since React v15.5. Please use [the `prop-types` library instead](https://www.npmjs.com/package/prop-types) to define `contextTypes`.
>
> We provide [a codemod script](/blog/2017/04/07/react-v15.5.0.html#migrating-from-react.proptypes) to automate the conversion.
### Parent-Child Coupling
> This section documents a legacy API. See the [new API](/docs/context.html).
Context can also let you build an API where parents and children communicate. For example, one library that works this way is [React Router V4](https://reacttraining.com/react-router):
```javascript
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
const BasicExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>
);
```
By passing down some information from the `Router` component, each `Link` and `Route` can communicate back to the containing `Router`.
Before you build components with an API similar to this, consider if there are cleaner alternatives. For example, you can pass entire React components as props if you'd like to.
### Referencing Context in Lifecycle Methods
> This section documents a legacy API. See the [new API](/docs/context.html).
If `contextTypes` is defined within a component, the following [lifecycle methods](/docs/react-component.html#the-component-lifecycle) will receive an additional parameter, the `context` object:
- [`constructor(props, context)`](/docs/react-component.html#constructor)
- [`componentWillReceiveProps(nextProps, nextContext)`](/docs/react-component.html#componentwillreceiveprops)
- [`shouldComponentUpdate(nextProps, nextState, nextContext)`](/docs/react-component.html#shouldcomponentupdate)
- [`componentWillUpdate(nextProps, nextState, nextContext)`](/docs/react-component.html#componentwillupdate)
> Note:
>
> As of React 16, `componentDidUpdate` no longer receives `prevContext`.
### Referencing Context in Stateless Functional Components
> This section documents a legacy API. See the [new API](/docs/context.html).
Stateless functional components are also able to reference `context` if `contextTypes` is defined as a property of the function. The following code shows a `Button` component written as a stateless functional component.
```javascript
import PropTypes from 'prop-types';
const Button = ({children}, context) =>
<button style={{background: context.color}}>
{children}
</button>;
Button.contextTypes = {color: PropTypes.string};
```
### Updating Context
> This section documents a legacy API. See the [new API](/docs/context.html).
Don't do it.
React has an API to update context, but it is fundamentally broken and you should not use it.
The `getChildContext` function will be called when the state or props changes. In order to update data in the context, trigger a local state update with `this.setState`. This will trigger a new context and changes will be received by the children.
```javascript
import PropTypes from 'prop-types';
class MediaQuery extends React.Component {
constructor(props) {
super(props);
this.state = {type:'desktop'};
}
getChildContext() {
return {type: this.state.type};
}
componentDidMount() {
const checkMediaQuery = () => {
const type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
if (type !== this.state.type) {
this.setState({type});
}
};
window.addEventListener('resize', checkMediaQuery);
checkMediaQuery();
}
render() {
return this.props.children;
}
}
MediaQuery.childContextTypes = {
type: PropTypes.string
};
```
The problem is, if a context value provided by component changes, descendants that use that value won't update if an intermediate parent returns `false` from `shouldComponentUpdate`. This is totally out of control of the components using context, so there's basically no way to reliably update the context. [This blog post](https://medium.com/@mweststrate/how-to-safely-use-react-context-b7e343eff076) has a good explanation of why this is a problem and how you might get around it.

4
content/docs/nav.yml

@ -66,6 +66,8 @@
title: Web Components
- id: higher-order-components
title: Higher-Order Components
- id: forwarding-refs
title: Forwarding Refs
- id: render-props
title: Render Props
- id: integrating-with-other-libraries
@ -74,6 +76,8 @@
title: Accessibility
- id: code-splitting
title: Code-Splitting
- id: strict-mode
title: Strict Mode
- title: Reference
items:
- id: react-api

89
content/docs/reference-react-component.md

@ -9,7 +9,7 @@ redirect_from:
- "docs/component-specs.html"
- "docs/component-specs-ko-KR.html"
- "docs/component-specs-zh-CN.html"
- "tips/componentWillReceiveProps-not-triggered-after-mounting.html"
- "tips/UNSAFE_componentWillReceiveProps-not-triggered-after-mounting.html"
- "tips/dom-event-listeners.html"
- "tips/initial-ajax.html"
- "tips/use-react-with-other-libraries.html"
@ -44,7 +44,8 @@ Each component has several "lifecycle methods" that you can override to run code
These methods are called when an instance of a component is being created and inserted into the DOM:
- [`constructor()`](#constructor)
- [`componentWillMount()`](#componentwillmount)
- [`static getDerivedStateFromProps()`](#static-getderivedstatefromprops)
- [`componentWillMount()` / `UNSAFE_componentWillMount()`](#unsafe_componentwillmount)
- [`render()`](#render)
- [`componentDidMount()`](#componentdidmount)
@ -52,10 +53,12 @@ These methods are called when an instance of a component is being created and in
An update can be caused by changes to props or state. These methods are called when a component is being re-rendered:
- [`componentWillReceiveProps()`](#componentwillreceiveprops)
- [`componentWillReceiveProps()` / `UNSAFE_componentWillReceiveProps()`](#unsafe_componentwillreceiveprops)
- [`static getDerivedStateFromProps()`](#static-getderivedstatefromprops)
- [`shouldComponentUpdate()`](#shouldcomponentupdate)
- [`componentWillUpdate()`](#componentwillupdate)
- [`componentWillUpdate()` / `UNSAFE_componentWillUpdate()`](#unsafe_componentwillupdate)
- [`render()`](#render)
- [`getSnapshotBeforeUpdate()`](#getsnapshotbeforeupdate)
- [`componentDidUpdate()`](#componentdidupdate)
#### Unmounting
@ -176,22 +179,40 @@ constructor(props) {
Beware of this pattern, as state won't be up-to-date with any props update. Instead of syncing props to state, you often want to [lift the state up](/docs/lifting-state-up.html) instead.
If you "fork" props by using them for state, you might also want to implement [`componentWillReceiveProps(nextProps)`](#componentwillreceiveprops) to keep the state up-to-date with them. But lifting state up is often easier and less bug-prone.
If you "fork" props by using them for state, you might also want to implement [`UNSAFE_componentWillReceiveProps(nextProps)`](#componentwillreceiveprops) to keep the state up-to-date with them. But lifting state up is often easier and less bug-prone.
* * *
### `componentWillMount()`
### `static getDerivedStateFromProps()`
```js
static getDerivedStateFromProps(nextProps, prevState)
```
`getDerivedStateFromProps` is invoked after a component is instantiated as well as when it receives new props. It should return an object to update state, or null to indicate that the new props do not require any state updates.
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. You may want to compare new and previous values if you only want to handle changes.
Calling `this.setState()` generally doesn't trigger `getDerivedStateFromProps()`.
* * *
### `UNSAFE_componentWillMount()`
```javascript
componentWillMount()
UNSAFE_componentWillMount()
```
`componentWillMount()` is invoked just before mounting occurs. It is called before `render()`, therefore calling `setState()` synchronously in this method will not trigger an extra rendering. Generally, we recommend using the `constructor()` instead.
`UNSAFE_componentWillMount()` is invoked just before mounting occurs. It is called before `render()`, therefore calling `setState()` synchronously in this method will not trigger an extra rendering. Generally, we recommend using the `constructor()` instead for initializing state.
Avoid introducing any side-effects or subscriptions in this method. For those use cases, use `componentDidMount()` instead.
This is the only lifecycle hook called on server rendering.
> Note
>
> This lifecycle was previously named `componentWillMount`. That name will continue to work until version 17. Use the [`rename-unsafe-lifecycles` codemod](https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles) to automatically update your components.
* * *
### `componentDidMount()`
@ -208,17 +229,25 @@ Calling `setState()` in this method will trigger an extra rendering, but it will
* * *
### `componentWillReceiveProps()`
### `UNSAFE_componentWillReceiveProps()`
```javascript
componentWillReceiveProps(nextProps)
UNSAFE_componentWillReceiveProps(nextProps)
```
`componentWillReceiveProps()` is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare `this.props` and `nextProps` and perform state transitions using `this.setState()` in this method.
> Note
>
> It is recommended that you use the static [`getDerivedStateFromProps`](#static-getderivedstatefromprops) lifecycle instead of `UNSAFE_componentWillReceiveProps`. [Learn more about this recommendation here.](/blog/2018/03/20/react-v-16-3.html#component-lifecycle-changes)
`UNSAFE_componentWillReceiveProps()` is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare `this.props` and `nextProps` and perform state transitions using `this.setState()` in this method.
Note that React will call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. Make sure to compare the current and next values if you only want to handle changes.
React doesn't call `componentWillReceiveProps()` with initial props during [mounting](#mounting). It only calls this method if some of component's props may update. Calling `this.setState()` generally doesn't trigger `componentWillReceiveProps()`.
React doesn't call `UNSAFE_componentWillReceiveProps()` with initial props during [mounting](#mounting). It only calls this method if some of component's props may update. Calling `this.setState()` generally doesn't trigger `UNSAFE_componentWillReceiveProps()`.
> Note
>
> This lifecycle was previously named `componentWillReceiveProps`. That name will continue to work until version 17. Use the [`rename-unsafe-lifecycles` codemod](https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles) to automatically update your components.
* * *
@ -234,7 +263,7 @@ Use `shouldComponentUpdate()` to let React know if a component's output is not a
Returning `false` does not prevent child components from re-rendering when *their* state changes.
Currently, if `shouldComponentUpdate()` returns `false`, then [`componentWillUpdate()`](#componentwillupdate), [`render()`](#render), and [`componentDidUpdate()`](#componentdidupdate) will not be invoked. Note that in the future React may treat `shouldComponentUpdate()` as a hint rather than a strict directive, and returning `false` may still result in a re-rendering of the component.
Currently, if `shouldComponentUpdate()` returns `false`, then [`UNSAFE_componentWillUpdate()`](#componentwillupdate), [`render()`](#render), and [`componentDidUpdate()`](#componentdidupdate) will not be invoked. Note that in the future React may treat `shouldComponentUpdate()` as a hint rather than a strict directive, and returning `false` may still result in a re-rendering of the component.
If you determine a specific component is slow after profiling, you may change it to inherit from [`React.PureComponent`](/docs/react-api.html#reactpurecomponent) which implements `shouldComponentUpdate()` with a shallow prop and state comparison. If you are confident you want to write it by hand, you may compare `this.props` with `nextProps` and `this.state` with `nextState` and return `false` to tell React the update can be skipped.
@ -242,34 +271,52 @@ We do not recommend doing deep equality checks or using `JSON.stringify()` in `s
* * *
### `componentWillUpdate()`
### `UNSAFE_componentWillUpdate()`
```javascript
componentWillUpdate(nextProps, nextState)
UNSAFE_componentWillUpdate(nextProps, nextState)
```
`componentWillUpdate()` is invoked just before rendering when new props or state are being received. Use this as an opportunity to perform preparation before an update occurs. This method is not called for the initial render.
`UNSAFE_componentWillUpdate()` is invoked just before rendering when new props or state are being received. Use this as an opportunity to perform preparation before an update occurs. This method is not called for the initial render.
Note that you cannot call `this.setState()` here; nor should you do anything else (e.g. dispatch a Redux action) that would trigger an update to a React component before `componentWillUpdate()` returns.
Note that you cannot call `this.setState()` here; nor should you do anything else (e.g. dispatch a Redux action) that would trigger an update to a React component before `UNSAFE_componentWillUpdate()` returns.
If you need to update `state` in response to `props` changes, use `componentWillReceiveProps()` instead.
If you need to update `state` in response to `props` changes, use `UNSAFE_componentWillReceiveProps()` instead.
> Note
>
> `componentWillUpdate()` will not be invoked if [`shouldComponentUpdate()`](#shouldcomponentupdate) returns false.
> This lifecycle was previously named `componentWillUpdate`. That name will continue to work until version 17. Use the [`rename-unsafe-lifecycles` codemod](https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles) to automatically update your components.
> Note
>
> `UNSAFE_componentWillUpdate()` will not be invoked if [`shouldComponentUpdate()`](#shouldcomponentupdate) returns false.
* * *
### `getSnapshotBeforeUpdate()`
`getSnapshotBeforeUpdate()` is invoked right before the most recently rendered output is committed to e.g. the DOM. It enables your component to capture current values (e.g. scroll position) before they are potential changed. Any value returned by this lifecycle will be passed as a parameter to `componentDidUpdate()`.
For example:
`embed:react-component-reference/get-snapshot-before-update.js`
In the above examples, it is important to read the `scrollHeight` property in `getSnapshotBeforeUpdate` rather than `componentWillUpdate` in order to support async rendering. With async rendering, there may be delays between "render" phase lifecycles (like `componentWillUpdate` and `render`) and "commit" phase lifecycles (like `getSnapshotBeforeUpdate` and `componentDidUpdate`). If a user does something like resize the browser during this time, a `scrollHeight` value read from `componentWillUpdate` will be stale.
* * *
### `componentDidUpdate()`
```javascript
componentDidUpdate(prevProps, prevState)
componentDidUpdate(prevProps, prevState, snapshot)
```
`componentDidUpdate()` is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).
If your component implements the `getSnapshotBeforeUpdate()` lifecycle, the value it returns will be passed as a third "snapshot" parameter to `componentDidUpdate()`. (Otherwise this parameter will be undefined.)
> Note
>
> `componentDidUpdate()` will not be invoked if [`shouldComponentUpdate()`](#shouldcomponentupdate) returns false.

11
content/docs/reference-react.md

@ -49,6 +49,10 @@ See [Using React without JSX](/docs/react-without-jsx.html) for more information
- [`React.Fragment`](#reactfragment)
### Other
- [`React.forwardRef`](#reactforwardref)
* * *
## Reference
@ -217,3 +221,10 @@ render() {
```
You can also use it with the shorthand `<></>` syntax. For more information, see [React v16.2.0: Improved Support for Fragments](/blog/2017/11/28/react-v16.2.0-fragment-support.html).
### `React.forwardRef`
`React.forwardRef` accepts a render function that receives `props` and `ref` parameters and returns a React node. Ref forwarding is a technique for passing a [ref](/docs/refs-and-the-dom.html) through a component to one of its descendants. This technique can be particularly useful with [higher-order components](/docs/higher-order-components.html):
`embed:reference-react-forward-ref.js`
For more information, see [forwarding refs](/docs/forwarding-refs.html).

207
content/docs/refs-and-the-dom.md

@ -11,6 +11,8 @@ redirect_from:
permalink: docs/refs-and-the-dom.html
---
Refs provide a way to access DOM nodes or React elements created in the render method.
In the typical React dataflow, [props](/docs/components-and-props.html) are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an instance of a React component, or it could be a DOM element. For both of these cases, React provides an escape hatch.
### When to Use Refs
@ -29,32 +31,69 @@ For example, instead of exposing `open()` and `close()` methods on a `Dialog` co
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. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. See the [Lifting State Up](/docs/lifting-state-up.html) guide for examples of this.
### Adding a Ref to a DOM Element
> Note
>
> The examples below have been updated to use the React.createRef() API introduced in React 16.3. If you are using an earlier release of React, we recommend using [callback refs](#callback-refs) instead.
### Creating Refs
Refs are created using `React.createRef()` and attached to React elements via the `ref` attribute. Refs are commonly assigned to an instance property when a component is constructed so they can be referenced throughout the the component.
```javascript{4,7}
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
```
### Accessing Refs
When a ref is passed to an element in `render`, a reference to the node becomes accessible at the `current` attribute of the ref.
React supports a special attribute that you can attach to any component. The `ref` attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted.
```javascript
const node = this.myRef.current
```
The value of the ref differs depending on the type of the node:
- When the `ref` attribute is used on an HTML element, the `ref` created in the constructor with `React.createRef()` receives the underlying DOM element as its `current` property.
- When the `ref` attribute is used on a custom class component, the `ref` object receives the mounted instance of the component as its `current`.
- **You may not use the `ref` attribute on functional components** because they don't have instances.
The examples below demonstrate the differences.
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:
#### Adding a Ref to a DOM Element
```javascript{8,9,19}
This code uses a `ref` to store a reference to a DOM node:
```javascript{5,12,22}
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
// tell React that we want the associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
@ -66,24 +105,26 @@ class CustomTextInput extends React.Component {
}
```
React will call the `ref` callback with the DOM element when the component mounts, and call it with `null` when it unmounts. `ref` callbacks are invoked before `componentDidMount` or `componentDidUpdate` lifecycle hooks.
Using the `ref` callback just to set a property on the class is a common pattern for accessing DOM elements. The preferred way is to set the property in the `ref` callback like in the above example. There is even a shorter way to write it: `ref={input => this.textInput = input}`.
React will assign the `current` property with the DOM element when the component mounts, and assign it back to `null` when it unmounts. `ref` updates happen before `componentDidMount` or `componentDidUpdate` lifecycle hooks.
### Adding a Ref to a Class Component
#### Adding a Ref to a Class Component
When the `ref` attribute is used on a custom component declared as a class, 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:
If we wanted to wrap the `CustomTextInput` above to simulate it being clicked immediately after mounting, we could use a ref to get access to the custom input and call its `focusTextInput` method manually:
```javascript{3,9}
```javascript{4,8,13}
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.focusTextInput();
this.textInput.current.focusTextInput();
}
render() {
return (
<CustomTextInput
ref={(input) => { this.textInput = input; }} />
<CustomTextInput ref={this.textInput} />
);
}
}
@ -97,21 +138,24 @@ class CustomTextInput extends React.Component {
}
```
### Refs and Functional Components
#### Refs and Functional Components
**You may not use the `ref` attribute on functional components** because they don't have instances:
```javascript{1,7}
```javascript{1,8,13}
function MyFunctionalComponent() {
return <input />;
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// This will *not* work!
return (
<MyFunctionalComponent
ref={(input) => { this.textInput = input; }} />
<MyFunctionalComponent ref={this.textInput} />
);
}
}
@ -123,25 +167,25 @@ You can, however, **use the `ref` attribute inside a functional component** as l
```javascript{2,3,6,13}
function CustomTextInput(props) {
// textInput must be declared here so the ref callback can refer to it
let textInput = null;
// textInput must be declared here so the ref can refer to it
let textInput = React.createRef();
function handleClick() {
textInput.focus();
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={(input) => { textInput = input; }} />
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
);
}
```
@ -151,11 +195,11 @@ In rare cases, you might want to have access to a child's DOM node from a parent
While you could [add a ref to the child component](#adding-a-ref-to-a-class-component), this is not an ideal solution, as you would only get a component instance rather than a DOM node. Additionally, this wouldn't work with functional components.
Instead, in such cases we recommend exposing a special prop on the child. The child would take a function prop with an arbitrary name (e.g. `inputRef`) and attach it to the DOM node as a `ref` attribute. This lets the parent pass its ref callback to the child's DOM node through the component in the middle.
Instead, in such cases we recommend exposing a special prop on the child. This prop can be named anything other than ref (e.g. inputRef). The child component can then forward the prop to the DOM node as a ref attribute. This lets the parent pass its ref to the child's DOM node through the component in the middle.
This works both for classes and for functional components.
```javascript{4,13}
```javascript{4,12,16}
function CustomTextInput(props) {
return (
<div>
@ -165,17 +209,19 @@ function CustomTextInput(props) {
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.inputElement = React.createRef();
}
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
<CustomTextInput inputRef={this.inputElement} />
);
}
}
```
In the example above, `Parent` passes its ref callback as an `inputRef` prop to the `CustomTextInput`, and the `CustomTextInput` passes the same function as a special `ref` attribute to the `<input>`. As a result, `this.inputElement` in `Parent` will be set to the DOM node corresponding to the `<input>` element in the `CustomTextInput`.
In the example above, `Parent` passes its class property `this.inputElement` as an `inputRef` prop to the `CustomTextInput`, and the `CustomTextInput` passes the same ref as a special `ref` attribute to the `<input>`. As a result, `this.inputElement.current` in `Parent` will be set to the DOM node corresponding to the `<input>` element in the `CustomTextInput`.
Note that the name of the `inputRef` prop in the above example has no special meaning, as it is a regular component prop. However, using the `ref` attribute on the `<input>` itself is important, as it tells React to attach a ref to its DOM node.
@ -183,7 +229,7 @@ This works even though `CustomTextInput` is a functional component. Unlike the s
Another benefit of this pattern is that it works several components deep. For example, imagine `Parent` didn't need that DOM node, but a component that rendered `Parent` (let's call it `Grandparent`) needed access to it. Then we could let the `Grandparent` specify the `inputRef` prop to the `Parent`, and let `Parent` "forward" it to the `CustomTextInput`:
```javascript{4,12,22}
```javascript{4,12,20,24}
function CustomTextInput(props) {
return (
<div>
@ -200,11 +246,90 @@ function Parent(props) {
);
}
class Grandparent extends React.Component {
constructor(props) {
super(props);
this.inputElement = React.createRef();
}
render() {
return (
<Parent
<Parent inputRef={this.inputElement} />
);
}
}
```
Here, the ref `this.inputElement` is first specified by `Grandparent`. It is passed to the `Parent` as a regular prop called `inputRef`, and the `Parent` passes it to the `CustomTextInput` as a prop too. Finally, the `CustomTextInput` reads the `inputRef` prop and attaches the passed ref as a `ref` attribute to the `<input>`. As a result, `this.inputElement.current` in `Grandparent` will be set to the DOM node corresponding to the `<input>` element in the `CustomTextInput`.
When possible, we advise against exposing DOM nodes, but it can be a useful escape hatch. Note that this approach requires you to add some code to the child component. If you have absolutely no control over the child component implementation, your last option is to use [`findDOMNode()`](/docs/react-dom.html#finddomnode), but it is discouraged.
### Callback Refs
React also supports another way to set refs called "callback refs", which gives more fine-grain control over when refs are set and unset.
Instead of passing a `ref` attribute created by `createRef()`, you pass a function. The function receives the React component instance or HTML DOM element as its argument, which can be stored and accessed elsewhere.
The example below implements a common pattern: using the `ref` callback to store a reference to a DOM node in an instance property.
```javascript{5,7-9,11-14,19,29,34}
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
this.focusTextInput = () => {
// Focus the text input using the raw DOM API
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// autofocus the input on mount
this.focusTextInput();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
return (
<div>
<input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
```
React will call the `ref` callback with the DOM element when the component mounts, and call it with `null` when it unmounts. `ref` callbacks are invoked before `componentDidMount` or `componentDidUpdate` lifecycle hooks.
You can pass callback refs between components like you can with object refs that were created with `React.createRef()`.
```javascript{4,13}
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
@ -212,14 +337,16 @@ class Grandparent extends React.Component {
}
```
Here, the ref callback is first specified by `Grandparent`. It is passed to the `Parent` as a regular prop called `inputRef`, and the `Parent` passes it to the `CustomTextInput` as a prop too. Finally, the `CustomTextInput` reads the `inputRef` prop and attaches the passed function as a `ref` attribute to the `<input>`. As a result, `this.inputElement` in `Grandparent` will be set to the DOM node corresponding to the `<input>` element in the `CustomTextInput`.
All things considered, we advise against exposing DOM nodes whenever possible, but this can be a useful escape hatch. Note that this approach requires you to add some code to the child component. If you have absolutely no control over the child component implementation, your last option is to use [`findDOMNode()`](/docs/react-dom.html#finddomnode), but it is discouraged.
In the example above, `Parent` passes its ref callback as an `inputRef` prop to the `CustomTextInput`, and the `CustomTextInput` passes the same function as a special `ref` attribute to the `<input>`. As a result, `this.inputElement` in `Parent` will be set to the DOM node corresponding to the `<input>` element in the `CustomTextInput`.
### Legacy API: String Refs
If you worked with React before, you might be familiar with an older API where the `ref` attribute is a string, like `"textInput"`, and the DOM node is accessed as `this.refs.textInput`. We advise against it because string refs have [some issues](https://github.com/facebook/react/pull/8333#issuecomment-271648615), are considered legacy, and **are likely to be removed in one of the future releases**. If you're currently using `this.refs.textInput` to access refs, we recommend the callback pattern instead.
If you worked with React before, you might be familiar with an older API where the `ref` attribute is a string, like `"textInput"`, and the DOM node is accessed as `this.refs.textInput`. We advise against it because string refs have [some issues](https://github.com/facebook/react/pull/8333#issuecomment-271648615), are considered legacy, and **are likely to be removed in one of the future releases**.
> Note
>
> If you're currently using `this.refs.textInput` to access refs, we recommend using either the [callback pattern](#callback-refs) or the [`createRef` API](#creating-refs) instead.
### Caveats
### Caveats with callback refs
If the `ref` callback is defined as an inline function, it will get called twice during updates, first with `null` and then again with the DOM element. This is because a new instance of the function is created with each render, so React needs to clear the old ref and set up the new one. You can avoid this by defining the `ref` callback as a bound method on the class, but note that it shouldn't matter in most cases.

4
content/docs/state-and-lifecycle.md

@ -249,9 +249,7 @@ The `componentDidMount()` hook runs after the component output has been rendered
Note how we save the timer ID right on `this`.
While `this.props` is set up by React itself and `this.state` has a special meaning, you are free to add additional fields to the class manually if you need to store something that is not used for the visual output.
If you don't use something in `render()`, it shouldn't be in the state.
While `this.props` is set up by React itself and `this.state` has a special meaning, you are free to add additional fields to the class manually if you need to store something that doesn’t participate in the data flow (like a timer ID).
We will tear down the timer in the `componentWillUnmount()` lifecycle hook:

88
content/docs/strict-mode.md

@ -0,0 +1,88 @@
---
id: strict-mode
title: Strict Mode
permalink: docs/strict-mode.html
---
`StrictMode` is a tool for highlighting potential problems in an application. Like `Fragment`, `StrictMode` does not render any visible UI. It activates additional checks and warnings for its descendants.
> Note:
>
> Strict mode checks are run in development mode only; _they do not impact the production build_.
You can enable strict mode for any part of your application. For example:
`embed:strict-mode/enabling-strict-mode.js`
In the above example, strict mode checks will *not* be run against the `Header` and `Footer` components. However, `ComponentOne` and `ComponentTwo`, as well as all of their descendants, will have the checks.
`StrictMode` currently helps with:
* [Identifying components with unsafe lifecycles](#identifying-unsafe-lifecycles)
* [Warning about legacy string ref API usage](#warning-about-legacy-string-ref-api-usage)
* [Detecting unexpected side effects](#detecting-unexpected-side-effects)
Additional functionality will be added with future releases of React.
### Identifying unsafe lifecycles
As explained [in this blog post](/blog/2018/03/27/update-on-async-rendering.html), certain legacy lifecycle methods are unsafe for use in async React applications. However, if your application uses third party libraries, it can be difficult to ensure that these lifecycles aren't being used. Fortunately, strict mode can help with this!
When strict mode is enabled, React compiles a list of all class components using the unsafe lifecycles, and logs a warning message with information about these components, like so:
![](../images/blog/strict-mode-unsafe-lifecycles-warning.png)
Addressing the issues identified by strict mode _now_ will make it easier for you to take advantage of async rendering in future releases of React.
### Warning about legacy string ref API usage
Previously, React provided two ways for managing refs: the legacy string ref API and the callback API. Although the string ref API was the more convenient of the two, it had [several downsides](https://github.com/facebook/react/issues/1373) and so our official recommendation was to [use the callback form instead](/docs/refs-and-the-dom.html#legacy-api-string-refs).
React 16.3 added a third option that offers the convenience of a string ref without any of the downsides:
`embed:16-3-release-blog-post/create-ref-example.js`
Since object refs were largely added as a replacement for string refs, strict mode now warns about usage of string refs.
> **Note:**
>
> Callback refs will continue to be supported in addition to the new `createRef` API.
>
> You don't need to replace callback refs in your components. They are slightly more flexible, so they will remain as an advanced feature.
[Learn more about the new `createRef` API here.](/docs/refs-and-the-dom.html)
### Detecting unexpected side effects
Conceptually, React does work in two phases:
* The **render** phase determines what changes need to be made to e.g. the DOM. During this phase, React calls `render` and then compares the result to the previous render.
* The **commit** phase is when React applies any changes. (In the case of React DOM, this is when React inserts, updates, and removes DOM nodes.) React also calls lifecycles like `componentDidMount` and `componentDidUpdate` during this phase.
The commit phase is usually very fast, but rendering can be slow. For this reason, the upcoming async mode (which is not enabled by default yet) breaks the rendering work into pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption).
Render phase lifecycles include the following class component methods:
* `constructor`
* `componentWillMount`
* `componentWillReceiveProps`
* `componentWillUpdate`
* `getDerivedStateFromProps`
* `shouldComponentUpdate`
* `render`
* `setState` updater functions (the first argument)
Because the above methods might be called more than once, it's important that they do not contain side-effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they can often be [non-deterministic](https://en.wikipedia.org/wiki/Deterministic_algorithm).
Strict mode can't automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following methods:
* Class component `constructor` method
* The `render` method
* `setState` updater functions (the first argument)
* The static `getDerivedStateFromProps` lifecycle
> Note:
>
> This only applies to development mode. _Lifecycles will not be double-invoked in production mode._
For example, consider the following code:
`embed:strict-mode/side-effects-in-constructor.js`
At first glance, this code might not seem problematic. But if `SharedApplicationState.recordEvent` is not [idempotent](https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning), then instantiating this component multiple times could lead to invalid application state. This sort of subtle bug might not manifest during development, or it might do so inconsistently and so be overlooked.
By intentionally double-invoking methods like the component constructor, strict mode makes patterns like this easier to spot.

BIN
content/images/blog/strict-mode-unsafe-lifecycles-warning.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

26
examples/16-3-release-blog-post/context-example.js

@ -0,0 +1,26 @@
// highlight-next-line
const ThemeContext = React.createContext('light');
class ThemeProvider extends React.Component {
state = {theme: 'light'};
render() {
// highlight-range{2-4}
return (
<ThemeContext.Provider value={this.state.theme}>
{this.props.children}
</ThemeContext.Provider>
);
}
}
class ThemedButton extends React.Component {
render() {
// highlight-range{2-4}
return (
<ThemeContext.Consumer>
{theme => <Button theme={theme} />}
</ThemeContext.Consumer>
);
}
}

14
examples/16-3-release-blog-post/create-ref-example.js

@ -0,0 +1,14 @@
class MyComponent extends React.Component {
// highlight-next-line
inputRef = React.createRef();
render() {
// highlight-next-line
return <input type="text" ref={this.inputRef} />;
}
componentDidMount() {
// highlight-next-line
this.inputRef.current.focus();
}
}

32
examples/16-3-release-blog-post/fancy-button-example.js

@ -0,0 +1,32 @@
class FancyButton extends React.Component {
buttonRef = React.createRef();
focus() {
this.buttonRef.current.focus();
}
render() {
// highlight-next-line
const {label, theme, ...rest} = this.props;
// highlight-range{5}
return (
<button
{...rest}
className={`${theme}-button`}
ref={this.buttonRef}>
{label}
</button>
);
}
}
// highlight-next-line
const FancyThemedButton = withTheme(FancyButton);
// We can render FancyThemedButton as if it were a FancyButton
// It will automatically receive the current "theme",
// And the HOC will pass through our other props.
<FancyThemedButton
label="Click me!"
onClick={handleClick}
/>;

36
examples/16-3-release-blog-post/forward-ref-example.js

@ -0,0 +1,36 @@
function withTheme(Component) {
// Note the second param "ref" provided by React.forwardRef.
// We can attach this to Component directly.
// highlight-range{1,5}
function ThemedComponent(props, ref) {
return (
<ThemeContext.Consumer>
{theme => (
<Component {...props} ref={ref} theme={theme} />
)}
</ThemeContext.Consumer>
);
}
// These next lines are not necessary,
// But they do give the component a better display name in DevTools,
// e.g. "ForwardRef(withTheme(MyComponent))"
// highlight-range{1-2}
const name = Component.displayName || Component.name;
ThemedComponent.displayName = `withTheme(${name})`;
// Tell React to pass the "ref" to ThemedComponent.
// highlight-next-line
return React.forwardRef(ThemedComponent);
}
// highlight-next-line
const fancyButtonRef = React.createRef();
// fancyButtonRef will now point to FancyButton
// highlight-range{4}
<FancyThemedButton
label="Click me!"
onClick={handleClick}
ref={fancyButtonRef}
/>;

10
examples/16-3-release-blog-post/hoc-theme-example.js

@ -0,0 +1,10 @@
function withTheme(Component) {
return function ThemedComponent(props) {
// highlight-range{2-4}
return (
<ThemeContext.Consumer>
{theme => <Component {...props} theme={theme} />}
</ThemeContext.Consumer>
);
};
}

11
examples/context/forwarding-refs-app.js

@ -0,0 +1,11 @@
import FancyButton from './fancy-button';
const ref = React.createRef();
// Our ref will point to the FancyButton component,
// And not the ThemeContext.Consumer that wraps it.
// This means we can call FancyButton methods like ref.current.focus()
// highlight-next-line
<FancyButton ref={ref} onClick={handleClick}>
Click me!
</FancyButton>;

16
examples/context/forwarding-refs-fancy-button.js

@ -0,0 +1,16 @@
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
// Use context to pass the current "theme" to FancyButton.
// Use forwardRef to pass refs to FancyButton as well.
// highlight-range{1,3}
export default React.forwardRef((props, ref) => (
<ThemeContext.Consumer>
{theme => <Button {...props} theme={theme} ref={ref} />}
</ThemeContext.Consumer>
));

10
examples/context/higher-order-component-before.js

@ -0,0 +1,10 @@
const ThemeContext = React.createContext('light');
function ThemedButton(props) {
// highlight-range{2-4}
return (
<ThemeContext.Consumer>
{theme => <button className={theme} {...props} />}
</ThemeContext.Consumer>
);
}

7
examples/context/higher-order-component-usage.js

@ -0,0 +1,7 @@
function Button({theme, ...rest}) {
// highlight-next-line
return <button className={theme} {...rest} />;
}
// highlight-next-line
const ThemedButton = withTheme(Button);

18
examples/context/higher-order-component.js

@ -0,0 +1,18 @@
const ThemeContext = React.createContext('light');
// This function takes a component...
// highlight-next-line
export function withTheme(Component) {
// ...and returns another component...
// highlight-next-line
return function ThemedComponent(props) {
// ... and renders the wrapped component with the context theme!
// Notice that we pass through any additional props as well
// highlight-range{2-4}
return (
<ThemeContext.Consumer>
{theme => <Component {...props} theme={theme} />}
</ThemeContext.Consumer>
);
};
}

28
examples/context/lifecycles.js

@ -0,0 +1,28 @@
class Button extends React.Component {
componentDidMount() {
// highlight-next-line
// ThemeContext value is this.props.theme
}
componentDidUpdate(prevProps, prevState) {
// highlight-range{1-2}
// Previous ThemeContext value is prevProps.theme
// New ThemeContext value is this.props.theme
}
render() {
const {theme, children} = this.props;
return (
<button className={theme ? 'dark' : 'light'}>
{children}
</button>
);
}
}
// highlight-range{3}
export default props => (
<ThemeContext.Consumer>
{theme => <Button {...props} theme={theme} />}
</ThemeContext.Consumer>
);

23
examples/context/motivation-problem.js

@ -0,0 +1,23 @@
function ThemedButton(props) {
//highlight-range{1}
return <Button theme={props.theme} />;
}
// An intermediate component
function Toolbar(props) {
// highlight-range{1-2,5}
// The Toolbar component must take an extra theme prop
// and pass it to the ThemedButton
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class App extends React.Component {
render() {
// highlight-range{1}
return <Toolbar theme="dark" />;
}
}

33
examples/context/motivation-solution.js

@ -0,0 +1,33 @@
// Create a theme context, defaulting to light theme
// highlight-next-line
const ThemeContext = React.createContext('light');
function ThemedButton(props) {
// highlight-range{1,3-5}
// The ThemedButton receives the theme from context
return (
<ThemeContext.Consumer>
{theme => <Button {...props} theme={theme} />}
</ThemeContext.Consumer>
);
}
// An intermediate component
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
class App extends React.Component {
render() {
// highlight-range{2,4}
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}

39
examples/context/multiple-contexts.js

@ -0,0 +1,39 @@
// Theme context, default to light theme
// highlight-next-line
const ThemeContext = React.createContext('light');
// Signed-in user context
// highlight-next-line
const UserContext = React.createContext();
// An intermediate component that depends on both contexts
function Toolbar(props) {
// highlight-range{2-10}
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
class App extends React.Component {
render() {
const {signedInUser, theme} = this.props;
// App component that provides initial context values
// highlight-range{2-3,5-6}
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Toolbar />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}

10
examples/context/reference-caveats-problem.js

@ -0,0 +1,10 @@
class App extends React.Component {
render() {
// highlight-range{2}
return (
<Provider value={{something: 'something'}}>
<Toolbar />
</Provider>
);
}
}

17
examples/context/reference-caveats-solution.js

@ -0,0 +1,17 @@
class App extends React.Component {
constructor(props) {
// highlight-range{2}
this.state = {
value: {something: 'something'},
};
}
render() {
// highlight-range{2}
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}

48
examples/context/theme-detailed-app.js

@ -0,0 +1,48 @@
import {ThemeContext, themes} from './theme-context';
import ThemedButton from './button';
// An intermediate component that uses the ThemedButton
function Toolbar(props) {
return (
<ThemedButton onClick={props.changeTheme}>
Change Theme
</ThemedButton>
);
}
class App extends React.Component {
constructor(props) {
this.state = {
theme: themes.light,
};
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
}
render() {
//highlight-range{1-3}
// The ThemedButton button inside the ThemeProvider
// uses the theme from state while the one outside uses
// the default dark theme
//highlight-range{3-5,7}
return (
<Page>
<ThemeContext.Provider value={this.state.theme}>
<Toolbar changeTheme={this.toggleTheme} />
</ThemeContext.Provider>
<Section>
<ThemedButton />
</Section>
</Page>
);
}
}
ReactDOM.render(<App />, document.root);

15
examples/context/theme-detailed-theme-context.js

@ -0,0 +1,15 @@
export const themes = {
light: {
foreground: '#ffffff',
background: '#222222',
},
dark: {
foreground: '#000000',
background: '#eeeeee',
},
};
// highlight-range{1-3}
export const ThemeContext = React.createContext(
themes.dark // default value
);

17
examples/context/theme-detailed-themed-button.js

@ -0,0 +1,17 @@
import {ThemeContext} from './theme-context';
function ThemedButton(props) {
// highlight-range{2-9}
return (
<ThemeContext.Consumer>
{theme => (
<button
{...props}
style={{backgroundColor: theme.background}}
/>
)}
</ThemeContext.Consumer>
);
}
export default ThemedButton;

15
examples/forwarding-refs/fancy-button-ref.js

@ -0,0 +1,15 @@
import FancyButton from './FancyButton';
// highlight-next-line
const ref = React.createRef();
// The FancyButton component we imported is the LogProps HOC.
// Even though the rendered output will be the same,
// Our ref will point to LogProps instead of the inner FancyButton component!
// This means we can't call e.g. ref.current.focus()
// highlight-range{4}
<FancyButton
label="Click Me"
handleClick={handleClick}
ref={ref}
/>;

12
examples/forwarding-refs/fancy-button.js

@ -0,0 +1,12 @@
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
// Rather than exporting FancyButton, we export LogProps.
// It will render a FancyButton though.
// highlight-next-line
export default logProps(FancyButton);

34
examples/forwarding-refs/log-props-after.js

@ -0,0 +1,34 @@
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
// highlight-next-line
const {forwardedRef, ...rest} = this.props;
// Assign the custom prop "forwardedRef" as a ref
// highlight-next-line
return <Component ref={forwardedRef} {...rest} />;
}
}
// Note the second param "ref" provided by React.forwardRef.
// We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
// And it can then be attached to the Component.
// highlight-range{1-3}
function forwardRef(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
// These next lines are not necessary,
// But they do give the component a better display name in DevTools,
// e.g. "ForwardRef(logProps(MyComponent))"
// highlight-range{1-2}
const name = Component.displayName || Component.name;
forwardRef.displayName = `logProps(${name})`;
return React.forwardRef(forwardRef);
}

16
examples/forwarding-refs/log-props-before.js

@ -0,0 +1,16 @@
// highlight-next-line
function logProps(WrappedComponent) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
// highlight-next-line
return <WrappedComponent {...this.props} />;
}
}
return LogProps;
}

27
examples/react-component-reference/get-snapshot-before-update.js

@ -0,0 +1,27 @@
class ScrollingList extends React.Component {
listRef = React.createRef();
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the current height of the list so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
return this.listRef.current.scrollHeight;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
if (snapshot !== null) {
this.listRef.current.scrollTop +=
this.listRef.current.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}

28
examples/reference-react-forward-ref.js

@ -0,0 +1,28 @@
function enhance(Component) {
class Enhanced extends React.Component {
// ...
render() {
const {forwardedRef, ...rest} = this.props;
// Assign the custom prop "forwardedRef" as a ref
// highlight-next-line
return <Component ref={forwardedRef} {...rest} />;
}
}
// Intercept the "ref" and pass it as a custom prop, e.g. "forwardedRef"
// highlight-range{1-3}
function enhanceForwardRef(props, ref) {
return <Enhanced {...props} forwardedRef={ref} />;
}
// These next lines are not necessary,
// But they do give the component a better display name in DevTools,
// e.g. "ForwardRef(withTheme(MyComponent))"
const name = Component.displayName || Component.name;
enhanceForwardRef.displayName = `enhance(${name})`;
// highlight-next-line
return React.forwardRef(enhanceForwardRef);
}

18
examples/strict-mode/enabling-strict-mode.js

@ -0,0 +1,18 @@
import React from 'react';
function ExampleApplication() {
return (
<div>
<Header />
{/* highlight-next-line */}
<React.StrictMode>
<div>
<ComponentOne />
<ComponentTwo />
</div>
{/* highlight-next-line */}
</React.StrictMode>
<Footer />
</div>
);
}

7
examples/strict-mode/side-effects-in-constructor.js

@ -0,0 +1,7 @@
class TopLevelRoute extends React.Component {
constructor(props) {
super(props);
SharedApplicationState.recordEvent('ExampleComponent');
}
}
Loading…
Cancel
Save