-
Notifications
You must be signed in to change notification settings - Fork 7.8k
16.3 release blog draft #587
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 64 commits
444896e
141de66
ce57928
da29d89
a55480b
935d2a1
ab62e88
08d444e
5a1d653
8494428
c08533a
33ff167
c09a284
ee78358
b049931
9bc1932
6627687
0e7243f
c07a6b0
7b02740
d7b34d4
8075ce2
96fe82c
96b10a7
962ef9a
4b939d2
04c7f16
b1adba0
9b869fa
566406e
a89e6b8
5e0901d
3f304b9
f6e5d65
6e35810
6a1ba97
1de2e33
c7a61e6
fecb153
866ef01
12d7c16
e5bf594
2f97f48
a357b64
a937c46
201fcad
2b1de01
22d0ec1
fe033f6
f14f147
5d5a5d2
2ead10e
29d6592
0780f72
a5022da
fe3d0a4
808e5bd
de22379
291b2fd
6aa1995
7c6688c
b49f99b
dbce488
2f9b77a
e9ae68f
53d604f
fec6d6c
7b5764f
0d32f18
7db107b
da091e6
6eb29a1
9f54997
4e1282e
f588bd6
ccc1f26
3af89c9
01c2691
f5d5139
5864093
d66d3a2
ef23c49
b25276a
c41bb29
1ead060
d58c892
e689824
509b126
fe84e09
9e4442a
3c44882
f9225e2
fb8e9e8
6919801
c9d50e3
7ac0712
920b4f4
349446d
b0e8604
8437e37
587ad93
f11a149
bd06bbc
b7b97c0
6d77836
e5cf31a
a29098e
57eb7cc
28b2b76
691cd45
032576c
797a792
3a36a40
e5ca13e
992a756
5a587d1
ac1d767
aa9810f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| --- | ||
| title: "React v16.3.0: New lifecycles and context API" | ||
| author: [bvaughn] | ||
| --- | ||
|
|
||
| This release includes a new class component lifecycle (`getDerivedStateFromProps`), a new `StrictMode` component, an official context API, 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. However, 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#api) | ||
|
|
||
| ## `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. (At Facebook, we maintain more than 50,000 React components, and we can't tell our engineers to rewrite them either.) Because of this, the migration path will be as gradual as possible, and will provide escape hatches. | ||
|
|
||
| > **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. | ||
|
|
||
| We are also adding a new static lifecycle, `getDerivedStateFromProps`, as a safer alternative to the legacy `componentWillReceiveProps`. | ||
|
|
||
| [Learn more about these lifecycle changes here.](#TODO) | ||
|
|
||
| ## `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) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,220 +4,92 @@ 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. | ||
|
|
||
| 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 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. | ||
|
|
||
| > 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. | ||
| Context provides a way to pass data through the component tree without having to pass props down manually at every level. | ||
|
|
||
| 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. | ||
|
|
||
| ## Why Not To Use Context | ||
| - [Motivation](#motivation) | ||
| - [API](#api) | ||
| - [React.createContext](#reactcreatecontext) | ||
| - [Provider](#provider) | ||
| - [Consumer](#consumer) | ||
| - [Examples](#examples) | ||
| - [Static Context](#static-context) | ||
| - [Dynamic Context](#dynamic-context) | ||
| - [Legacy API](#legacy-api) | ||
|
|
||
| The vast majority of applications do not need 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. | ||
| ## Motivation | ||
|
|
||
| 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. | ||
| Context is designed to relieve the pain of passing props down through a deeply nested component tree. For example, in the code below we manually thread through a color prop in order to style the Button and Message components: | ||
|
|
||
| If you're still learning React, don't use context. There is usually a better way to implement functionality just using props and state. | ||
| `embed:context/motivation-problem.js` | ||
|
|
||
| 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. | ||
| Using context, we can avoid passing props through intermediate elements: | ||
|
|
||
| ## How To Use Context | ||
| `embed:context/motivation-solution.js` | ||
|
|
||
| Suppose you have a structure like: | ||
| ## API | ||
|
|
||
| ```javascript | ||
| class Button extends React.Component { | ||
| render() { | ||
| return ( | ||
| <button style={{background: this.props.color}}> | ||
| {this.props.children} | ||
| </button> | ||
| ); | ||
| } | ||
| } | ||
| ### `React.createContext` | ||
|
|
||
| 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 | ||
| }; | ||
| ```js | ||
| const {Provider, Consumer} = React.createContext(defaultValue); | ||
| ``` | ||
|
|
||
| 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`. | ||
| Creates a `{ Provider, Consumer }` pair. | ||
|
|
||
| If `contextTypes` is not defined, then `context` will be an empty object. | ||
| Optionally accepts a default value to be passed to Consumers without a Provider ancestor. | ||
|
|
||
| ## Parent-Child Coupling | ||
| ### `Provider` | ||
|
|
||
| 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): | ||
| ```js | ||
| <Provider value={/* some value */}> | ||
| ``` | ||
|
|
||
| ```javascript | ||
| import { BrowserRouter as Router, Route, Link } from 'react-router-dom'; | ||
| A React component that allows Consumers to subscribe to context changes. | ||
|
|
||
| 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> | ||
| 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. | ||
|
|
||
| <hr /> | ||
| ### `Consumer` | ||
|
|
||
| <Route exact path="/" component={Home} /> | ||
| <Route path="/about" component={About} /> | ||
| <Route path="/topics" component={Topics} /> | ||
| </div> | ||
| </Router> | ||
| ); | ||
| ```js | ||
| <Consumer> | ||
| { value => { /* render something based on the context value */ } } | ||
|
||
| </Consumer> | ||
| ``` | ||
|
|
||
| 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. | ||
| A React component that subscribes to context changes. | ||
|
|
||
| ## Referencing Context in Lifecycle Methods | ||
|
|
||
| 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) | ||
| Requires a [function as a child](/docs/render-props.html#using-props-other-than-render). This function receives the current context value and returns a React node. It will be called whenever the Provider's value is updated. | ||
|
||
|
|
||
| > Note: | ||
| > | ||
| > As of React 16, `componentDidUpdate` no longer receives `prevContext`. | ||
|
|
||
| ## Referencing Context in Stateless Functional Components | ||
|
|
||
| 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'; | ||
| > | ||
| > For more information about this pattern, see [render props](/docs/render-props.html). | ||
|
|
||
| const Button = ({children}, context) => | ||
| <button style={{background: context.color}}> | ||
| {children} | ||
| </button>; | ||
| ## Examples | ||
|
|
||
| Button.contextTypes = {color: PropTypes.string}; | ||
| ``` | ||
|
|
||
| ## Updating Context | ||
|
|
||
| Don't do it. | ||
| ### Static Context | ||
|
|
||
| React has an API to update context, but it is fundamentally broken and you should not use it. | ||
| Here is an example illustrating how you might inject a "theme" using context: | ||
|
|
||
| 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/theme-example.js` | ||
|
|
||
| ```javascript | ||
| import PropTypes from 'prop-types'; | ||
| ### Dynamic Context | ||
|
|
||
| class MediaQuery extends React.Component { | ||
| constructor(props) { | ||
| super(props); | ||
| this.state = {type:'desktop'}; | ||
| } | ||
| A more complex example with dynamic values for the theme: | ||
|
|
||
| getChildContext() { | ||
| return {type: this.state.type}; | ||
| } | ||
| **theme-context.js** | ||
| `embed:context/theme-detailed-theme-context.js` | ||
|
|
||
| componentDidMount() { | ||
| const checkMediaQuery = () => { | ||
| const type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile'; | ||
| if (type !== this.state.type) { | ||
| this.setState({type}); | ||
| } | ||
| }; | ||
| **themed-button.js** | ||
| `embed:context/theme-detailed-themed-button.js` | ||
|
|
||
| window.addEventListener('resize', checkMediaQuery); | ||
| checkMediaQuery(); | ||
| } | ||
| **app.js** | ||
| `embed:context/theme-detailed-app.js` | ||
|
|
||
| render() { | ||
| return this.props.children; | ||
| } | ||
| } | ||
|
|
||
| MediaQuery.childContextTypes = { | ||
| type: PropTypes.string | ||
| }; | ||
| ``` | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There needs to be a section on how to reference context in lifecycle methods. It can be deduced from this article but people will initially feel it's gross so we need to be explicit that it's a recommended pattern. Additionally it's important to show how it works with ref forwarding.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There should also be an explanation on how to consume more than one context. Even if it links to a gist. |
||
| ## Legacy API | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before "Legacy API", we need to add a "Caveats" section. There, we should mention that pattern like
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to put this in the Consumer API section?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe. What do you think? I generally prefer to separate "minimal necessary" information to use the API from all the details (that they user won't bump into later on), but maybe it's okay to do in one place. The main reason I suggested a separate section is because I wanted to have a "good" and "bad" example (even if they're one liners), and I felt it would they would steal the focus from the API reference if we put them there.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, how about linking to Caveats/Gotchas after mentioning the Object.is comparison?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good |
||
|
|
||
| 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. | ||
| > The legacy context API was deprecated in React 16.3 and will be removed in version 17. | ||
|
||
| > | ||
| > 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. Read the [legacy context docs here](/docs/legacy-context.html). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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` | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I anticipate that people new to React will assume from this paragraph that you need to use context as soon as you pass a prop down more than a few levels. I don't think we want to encourage that.
I think this should be more specific and say that context is for data that can be considered "global" for a tree of React components, and immediately give a few examples: currently authenticated user, current theme, current language.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add a section "When to Use Context" that is very clear about intended use cases for this feature, as well as use cases where we don't recommend it (e.g. don't just use it to avoid passing props a few levels down).