From 57a23173497934f7792827f8fabaec2acd77bee2 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 11:39:09 -0700 Subject: [PATCH 01/17] [WIP] 16.4 blog post --- content/blog/2018-03-29-react-v-16-4.md | 128 ++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 content/blog/2018-03-29-react-v-16-4.md diff --git a/content/blog/2018-03-29-react-v-16-4.md b/content/blog/2018-03-29-react-v-16-4.md new file mode 100644 index 00000000000..b11569fc9cd --- /dev/null +++ b/content/blog/2018-03-29-react-v-16-4.md @@ -0,0 +1,128 @@ +--- +title: "React v16.4.0: Experimental Component Profiler and Bugfixes" +author: [acdlite, bvaughn] +--- + +React 16.4 is a minor release that includes an experimental component profiler, as well as a crucial bugfix for `getDerivedStateFromProps`. + +## `React.unstable_Profiler` + +TODO (@bvaughn) + +## Bugfix for `getDerivedStateFromProps` + +`getDerivedStateFromProps` is now called when either props or state change. Previously, it would not fire for state-only changes (similar to `componentWillReceiveProps`). This was an oversight in the initial implementation that has now been corrected. + +### Why? + +`getDerivedStateFromProps` was designed as an alternative to `componentWillReceiveProps` that is compatible with async rendering. Because props in React are derived from state that lives in a parent component, APIs like `componentWillReceiveProps` that are modelled around "prop change events" don't really make sense, and can't be reliably implemented in React's upcoming asynchronous rendering mode. + +Another way to think about it is that `getDerivedStateFromProps(props, state)` accepts both props and state as input, so we need to re-calculate the derived state whenever either of the inputs change. + +### How Does This Affect Me? + +**Most likely, this doesn't require any changes to your components**. The rare cases where it does matter usually fall into one of two categories: + +### Avoid Side Effects in `getDerivedStateFromProps` + +Like the render method, `getDerivedStateFromProps` should be a pure function of props and state. You should move side-effectful code elsewhere. E.g. Flux dispatches typically belong inside the originating event handler; DOM mutations belong inside `componentDidMount` or `componentDidUpdate`. + +Side effects in `getDerivedStateFromProps` were already disallowed, but since it now fires more often than it used to, the recent change may expose previously undiscovered bugs. + +### Compare Incoming Props to Previous Props When Computing Controlled Values + +The following code will not work. It assumes `getDerivedStateFromProps` only fires on prop changes. But since it now fires for state changes, too, local updates to the controlled value will be ignored, since the props version always overrides it. + +```js +static getDerivedStateFromProps(props, state) { + if (props.value !== state.controlledValue) { + return { + // Props always win. Oops! + controlledValue: props.value, + }; + } + return null; +} +``` + +You can change it to do this instead: + +```js +static getDerivedStateFromProps(props, state) { + const prevProps = state.prevProps; + // Compare incoming props to previous props + const controlledValue = + prevProps.value !== state.controlledValue + ? prevProps.value + : state.controlledValue; + return { + // Store the previous props in state + prevProps: props, + controlledValue, + }; +} +``` + +## Installation + +React v16.4.0 is available on the npm registry. + +To install React 16 with Yarn, run: + +```bash +yarn add react@^16.4.0 react-dom@^16.4.0 +``` + +To install React 16 with npm, run: + +```bash +npm install --save react@^16.4.0 react-dom@^16.4.0 +``` + +We also provide UMD builds of React via a CDN: + +```html + + +``` + +Refer to the documentation for [detailed installation instructions](/docs/installation.html). + +## Changelog + +### React + +* Add a new [experimental](https://github.com/reactjs/rfcs/pull/51) `React.unstable_Profiler` component for measuring performance. ([@bvaughn](https://github.com/bvaughn) in [#12745](https://github.com/facebook/react/pull/12745)) + +### React DOM + +* Add support for the Pointer Events specification. ([@philipp-spiess](https://github.com/philipp-spiess) in [#12507](https://github.com/facebook/react/pull/12507)) +* Properly call `getDerivedStateFromProps()` regardless of the reason for re-rendering. ([@acdlite](https://github.com/acdlite) in [#12600](https://github.com/facebook/react/pull/12600) and [#12802](https://github.com/facebook/react/pull/12802)) +* Fix a bug that prevented context propagation in some cases. ([@gaearon](https://github.com/gaearon) in [#12708](https://github.com/facebook/react/pull/12708)) +* Fix re-rendering of components using `forwardRef()` on a deeper `setState()`. ([@gaearon](https://github.com/gaearon) in [#12690](https://github.com/facebook/react/pull/12690)) +* Fix some attributes incorrectly getting removed from custom element nodes. ([@airamrguez](https://github.com/airamrguez) in [#12702](https://github.com/facebook/react/pull/12702)) +* Fix context providers to not bail out on children if there's a legacy context provider above. ([@gaearon](https://github.com/gaearon) in [#12586](https://github.com/facebook/react/pull/12586)) +* Add the ability to specify `propTypes` on a context provider component. ([@nicolevy](https://github.com/nicolevy) in [#12658](https://github.com/facebook/react/pull/12658)) +* Fix a false positive warning when using `react-lifecycles-compat` in ``. ([@bvaughn](https://github.com/bvaughn) in [#12644](https://github.com/facebook/react/pull/12644)) +* Warn when the `forwardRef()` render function has `propTypes` or `defaultProps`. ([@bvaughn](https://github.com/bvaughn) in [#12644](https://github.com/facebook/react/pull/12644)) +* Improve how `forwardRef()` and context consumers are displayed in the component stack. ([@sophiebits](https://github.com/sophiebits) in [#12777](https://github.com/facebook/react/pull/12777)) +* Change internal event names. This can break third-party packages that rely on React internals in unsupported ways. ([@philipp-spiess](https://github.com/philipp-spiess) in [#12629](https://github.com/facebook/react/pull/12629)) + +### React Test Renderer + +* Fix the `getDerivedStateFromProps()` support to match the new React DOM behavior. ([@koba04](https://github.com/koba04) in [#12676](https://github.com/facebook/react/pull/12676)) +* Fix a `testInstance.parent` crash when the parent is a fragment or another special node. ([@gaearon](https://github.com/gaearon) in [#12813](https://github.com/facebook/react/pull/12813)) +* `forwardRef()` components are now discoverable by the test renderer traversal methods. ([@gaearon](https://github.com/gaearon) in [#12725](https://github.com/facebook/react/pull/12725)) +* Shallow renderer now ignores `setState()` updaters that return `null` or `undefined`. ([@koba04](https://github.com/koba04) in [#12756](https://github.com/facebook/react/pull/12756)) + +### React ART + +* Fix reading context provided from the tree managed by React DOM. ([@acdlite](https://github.com/acdlite) in [#12779](https://github.com/facebook/react/pull/12779)) + +### React Call Return (Experimental) + +* This experiment was deleted because it was affecting the bundle size and the API wasn't good enough. It's likely to come back in the future in some other form. ([@gaearon](https://github.com/gaearon) in [#12820](https://github.com/facebook/react/pull/12820)) + +### React Reconciler (Experimental) + +* The [new host config shape](https://github.com/facebook/react/blob/c601f7a64640290af85c9f0e33c78480656b46bc/packages/react-noop-renderer/src/createReactNoop.js#L82-L285) is flat and doesn't use nested objects. ([@gaearon](https://github.com/gaearon) in [#12792](https://github.com/facebook/react/pull/12792)) From a3cff20422060c0a1c24684abecdf387e750da38 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 12:23:58 -0700 Subject: [PATCH 02/17] Update date in filename --- .../{2018-03-29-react-v-16-4.md => 2018-05-23-react-v-16-4.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename content/blog/{2018-03-29-react-v-16-4.md => 2018-05-23-react-v-16-4.md} (100%) diff --git a/content/blog/2018-03-29-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md similarity index 100% rename from content/blog/2018-03-29-react-v-16-4.md rename to content/blog/2018-05-23-react-v-16-4.md From 6a453171a618f99289f2338ffef104ddef6774b2 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 12:58:58 -0700 Subject: [PATCH 03/17] Fix controlled state example --- content/blog/2018-05-23-react-v-16-4.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index b11569fc9cd..c515c8eb06a 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -52,8 +52,8 @@ static getDerivedStateFromProps(props, state) { const prevProps = state.prevProps; // Compare incoming props to previous props const controlledValue = - prevProps.value !== state.controlledValue - ? prevProps.value + prevProps.value !== props.value + ? props.value : state.controlledValue; return { // Store the previous props in state From 69d46aa3e08dff4f7759af90580e6d571fc9cfe8 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 14:05:20 -0700 Subject: [PATCH 04/17] Tweak gDSFP section --- content/blog/2018-05-23-react-v-16-4.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index c515c8eb06a..bd12c901b58 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -11,17 +11,9 @@ TODO (@bvaughn) ## Bugfix for `getDerivedStateFromProps` -`getDerivedStateFromProps` is now called when either props or state change. Previously, it would not fire for state-only changes (similar to `componentWillReceiveProps`). This was an oversight in the initial implementation that has now been corrected. +`getDerivedStateFromProps` is now called every time a component is rendered, regardless of the cause of the update. Previously, it was only called if the component was re-rendered by its parent; it would not fire as the result of a local `setState`. This was an oversight in the initial implementation that has now been corrected. The previous behavior was more similar to how `componentWillReceiveProps` works, but the improved behavior ensures compatibility with React's upcoming asynchronous rendering mode. -### Why? - -`getDerivedStateFromProps` was designed as an alternative to `componentWillReceiveProps` that is compatible with async rendering. Because props in React are derived from state that lives in a parent component, APIs like `componentWillReceiveProps` that are modelled around "prop change events" don't really make sense, and can't be reliably implemented in React's upcoming asynchronous rendering mode. - -Another way to think about it is that `getDerivedStateFromProps(props, state)` accepts both props and state as input, so we need to re-calculate the derived state whenever either of the inputs change. - -### How Does This Affect Me? - -**Most likely, this doesn't require any changes to your components**. The rare cases where it does matter usually fall into one of two categories: +**Most likely, this doesn't require any changes to your components**. The rare cases where it does matter fall into one of two categories: ### Avoid Side Effects in `getDerivedStateFromProps` @@ -31,7 +23,7 @@ Side effects in `getDerivedStateFromProps` were already disallowed, but since it ### Compare Incoming Props to Previous Props When Computing Controlled Values -The following code will not work. It assumes `getDerivedStateFromProps` only fires on prop changes. But since it now fires for state changes, too, local updates to the controlled value will be ignored, since the props version always overrides it. +The following code assumes `getDerivedStateFromProps` only fires on prop changes. But since it now fires for state changes, too, local updates to the controlled value will be ignored, since the props version always overrides it. ```js static getDerivedStateFromProps(props, state) { From 988a67739d45328cdaf108ff0ea296ce0e285c76 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Wed, 11 Apr 2018 13:01:24 +0200 Subject: [PATCH 05/17] Add section about Pointer Events This PR adds a section about the state of Pointer Events in React. This should be merged only if facebook/react#12507 is accepted as well. --- content/docs/reference-events.md | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/content/docs/reference-events.md b/content/docs/reference-events.md index 828b63917e3..7e5ea81562c 100644 --- a/content/docs/reference-events.md +++ b/content/docs/reference-events.md @@ -76,6 +76,7 @@ The event handlers below are triggered by an event in the bubbling phase. To reg - [Focus Events](#focus-events) - [Form Events](#form-events) - [Mouse Events](#mouse-events) +- [Pointer Events](#pointer-events) - [Selection Events](#selection-events) - [Touch Events](#touch-events) - [UI Events](#ui-events) @@ -215,6 +216,40 @@ boolean shiftKey * * * +### Pointer Events + +Event names: + +``` +onPointerDown onPointerMove onPointerUp onPointerCancel onGotPointerCapture +onLostPointerCapture onPointerEnter onPointerLeave onPointerOver onPointerOut +``` + +The `onPointerEnter` and `onPointerLeave` events propagate from the element being left to the one being entered instead of ordinary bubbling and do not have a capture phase. + +Properties: + +As defined in the [W3 spec](https://www.w3.org/TR/pointerevents/), pointer events extend [Mouse Events](#mouse-events) with the following properties: + +```javascript +number pointerId +number width +number height +number pressure +number tiltX +number tiltY +string pointerType +boolean isPrimary +``` + +A note on cross-browser support: + +Pointer events are not yet supported in every browser (at the time of writing this article, supported browsers include: Chrome, Firefox, Edge, and Internet Explorer). React deliberately does not polyfill support for other browsers because a standard-conform polyfill would significantly increase the bundle size of `react-dom`. + +If your application requires pointer events, we recommend adding a third party pointer event polyfill like [PEP](https://github.com/jquery/PEP). + +* * * + ### Selection Events Event names: From a1f41f65a24e64bab5e97cbac463f66e65f5215b Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Wed, 16 May 2018 15:03:17 +0200 Subject: [PATCH 06/17] =?UTF-8?q?Don=E2=80=99t=20recommend=20PEP=20because?= =?UTF-8?q?=20it=20lacks=20features?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/docs/reference-events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/reference-events.md b/content/docs/reference-events.md index 7e5ea81562c..711b244197b 100644 --- a/content/docs/reference-events.md +++ b/content/docs/reference-events.md @@ -246,7 +246,7 @@ A note on cross-browser support: Pointer events are not yet supported in every browser (at the time of writing this article, supported browsers include: Chrome, Firefox, Edge, and Internet Explorer). React deliberately does not polyfill support for other browsers because a standard-conform polyfill would significantly increase the bundle size of `react-dom`. -If your application requires pointer events, we recommend adding a third party pointer event polyfill like [PEP](https://github.com/jquery/PEP). +If your application requires pointer events, we recommend adding a third party pointer event polyfill. * * * From 1dfdfd85956f228a56fcf1348b49171bd9498ee9 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 15:22:15 -0700 Subject: [PATCH 07/17] Pointer events section --- content/blog/2018-05-23-react-v-16-4.md | 27 ++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index bd12c901b58..889d6db6ed8 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -1,13 +1,30 @@ --- -title: "React v16.4.0: Experimental Component Profiler and Bugfixes" -author: [acdlite, bvaughn] +title: "React v16.4.0: Support for Pointer Events" +author: [acdlite] --- -React 16.4 is a minor release that includes an experimental component profiler, as well as a crucial bugfix for `getDerivedStateFromProps`. +React 16.4 adds an oft-requested feature: pointer events! -## `React.unstable_Profiler` +It also includes a bugfix for `getDerivedStateFromProps`. Check out the full [changelog](#changelog) below. -TODO (@bvaughn) +## Pointer Events + +The following event types are now available in React DOM: + +- `onPointerDown` +- `onPointerMove` +- `onPointerUp` +- `onPointerCancel` +- `onGotPointerCapture` +- `onLostPointerCapture` +- `onPointerEnter` +- `onPointerLeave` +- `onPointerOver` +- `onPointerOut` + +Please note that these events will only work in browsers that support the Pointer Events specification. (At the time of this writing, this includes the latest versions of Chrome, Firefox, Edge, and Internet Explorer.) If your application depends on pointer events, we recommend using a third-party pointer events polyfill; we have opted not to include such a polyfill in React DOM, since it would bloat the bundle size. + +Huge thanks to [Philip Spiess](https://github.com/philipp-spiess) for contributing this change! ## Bugfix for `getDerivedStateFromProps` From 983476e3f3ba7dc9ab18c7d3f331c55a7d676e23 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 15:31:27 -0700 Subject: [PATCH 08/17] Update getDerivedStateFromProps docs --- content/docs/reference-react-component.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/reference-react-component.md b/content/docs/reference-react-component.md index 58b01238844..0a5bdc99579 100644 --- a/content/docs/reference-react-component.md +++ b/content/docs/reference-react-component.md @@ -189,9 +189,9 @@ If you "fork" props by using them for state, you might also want to implement [` 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. +`getDerivedStateFromProps` is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing. -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. +Note that this method is fired on *every* render, regardless of the cause. This is in contrast to `UNSAFE_componentWillReceiveProps`, which only fires when the parent causes a re-render and not as a result of a local `setState`. * * * From 3c3646139e3eb8d5ef7bafc2a751ba2686b6a803 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 23 May 2018 15:33:41 -0700 Subject: [PATCH 09/17] Updated gatsby-config to add Code Sandbox dependencies --- gatsby-config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/gatsby-config.js b/gatsby-config.js index fe4410e959d..703f488b120 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -66,6 +66,7 @@ module.exports = { `//unpkg.com/react/umd/react.development.js`, `//unpkg.com/react-dom/umd/react-dom.development.js`, ], + dependencies: [`react`, `react-dom`], redirectTemplate: `${__dirname}/src/templates/codepen-example.js`, target: '_blank', }, From 88fb23580dfd06ed1b23961a9b96822acef53c0b Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 16:34:21 -0700 Subject: [PATCH 10/17] More edits --- content/blog/2018-05-23-react-v-16-4.md | 32 +++++++++++++++---------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index 889d6db6ed8..c980e2a9159 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -1,9 +1,9 @@ --- -title: "React v16.4.0: Support for Pointer Events" +title: "React v16.4.0: Pointer Events" author: [acdlite] --- -React 16.4 adds an oft-requested feature: pointer events! +The latest minor release adds support for an oft-requested feature: pointer events! It also includes a bugfix for `getDerivedStateFromProps`. Check out the full [changelog](#changelog) below. @@ -22,31 +22,35 @@ The following event types are now available in React DOM: - `onPointerOver` - `onPointerOut` -Please note that these events will only work in browsers that support the Pointer Events specification. (At the time of this writing, this includes the latest versions of Chrome, Firefox, Edge, and Internet Explorer.) If your application depends on pointer events, we recommend using a third-party pointer events polyfill; we have opted not to include such a polyfill in React DOM, since it would bloat the bundle size. +Please note that these events will only work in browsers that support the Pointer Events specification. (At the time of this writing, this includes the latest versions of Chrome, Firefox, Edge, and Internet Explorer.) If your application depends on pointer events, we recommend using a third-party pointer events polyfill. We have opted not to include such a polyfill in React DOM, to avoid an increase in bundle size. Huge thanks to [Philip Spiess](https://github.com/philipp-spiess) for contributing this change! ## Bugfix for `getDerivedStateFromProps` -`getDerivedStateFromProps` is now called every time a component is rendered, regardless of the cause of the update. Previously, it was only called if the component was re-rendered by its parent; it would not fire as the result of a local `setState`. This was an oversight in the initial implementation that has now been corrected. The previous behavior was more similar to how `componentWillReceiveProps` works, but the improved behavior ensures compatibility with React's upcoming asynchronous rendering mode. +`getDerivedStateFromProps` is now called every time a component is rendered, regardless of the cause of the update. Previously, it was only called if the component was re-rendered by its parent, and would not fire as the result of a local `setState`. This was an oversight in the initial implementation that has now been corrected. The previous behavior was more similar to `componentWillReceiveProps`, but the improved behavior ensures compatibility with React's upcoming asynchronous rendering mode. -**Most likely, this doesn't require any changes to your components**. The rare cases where it does matter fall into one of two categories: +**Most likely, this doesn't require any changes to your components**. The rare cases where it does matter fall into one of two categories. -### Avoid Side Effects in `getDerivedStateFromProps` +### 1. Avoid Side Effects in `getDerivedStateFromProps` -Like the render method, `getDerivedStateFromProps` should be a pure function of props and state. You should move side-effectful code elsewhere. E.g. Flux dispatches typically belong inside the originating event handler; DOM mutations belong inside `componentDidMount` or `componentDidUpdate`. +Like the render method, `getDerivedStateFromProps` should be a pure function of props and state. You should move side-effectful code elsewhere. E.g. Flux dispatches typically belong inside the originating event handler. DOM mutations belong inside `componentDidMount` or `componentDidUpdate`. Side effects in `getDerivedStateFromProps` were already disallowed, but since it now fires more often than it used to, the recent change may expose previously undiscovered bugs. -### Compare Incoming Props to Previous Props When Computing Controlled Values +You can read more about this in our recent post about [preparing for asynchronous rendering](/blog/2018/03/27/update-on-async-rendering.html). -The following code assumes `getDerivedStateFromProps` only fires on prop changes. But since it now fires for state changes, too, local updates to the controlled value will be ignored, since the props version always overrides it. +### 2. Compare Incoming Props to Previous Props When Computing Controlled Values + +The following code assumes `getDerivedStateFromProps` only fires on prop changes: ```js static getDerivedStateFromProps(props, state) { if (props.value !== state.controlledValue) { return { - // Props always win. Oops! + // Since this method fires on both props and state changes, local updates + // to the controlled value will be ignored, since the props version always + // overrides it. controlledValue: props.value, }; } @@ -54,12 +58,12 @@ static getDerivedStateFromProps(props, state) { } ``` -You can change it to do this instead: +One possible way to fix this is to compare the incoming value to the previous value by storing the previous props in state: ```js static getDerivedStateFromProps(props, state) { const prevProps = state.prevProps; - // Compare incoming props to previous props + // Compare the incoming prop to previous prop const controlledValue = prevProps.value !== props.value ? props.value @@ -72,6 +76,10 @@ static getDerivedStateFromProps(props, state) { } ``` +However, try to avoid mixing props and state in this way. Prefer to have one source of truth for any value, and [lift state up](/docs/lifting-state-up.html) if necessary to share it between multiple components. + +Please remember that **most components do not need `getDerivedStateFromProps`**. It's not meant to serve as a one-to-one replacement for `componentWillReceiveProps`. We'll publish a follow-up post in the coming weeks with more recommendations on how to use (and not use) `getDerivedStateFromProps`. + ## Installation React v16.4.0 is available on the npm registry. From 55b251146d9c4b77529fba033113ea46195b066e Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 16:41:51 -0700 Subject: [PATCH 11/17] Add pointer events example --- content/blog/2018-05-23-react-v-16-4.md | 2 + .../pointer-events-example.js | 95 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 examples/16-4-release-blog-post/pointer-events-example.js diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index c980e2a9159..7de02c51dc4 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -24,6 +24,8 @@ The following event types are now available in React DOM: Please note that these events will only work in browsers that support the Pointer Events specification. (At the time of this writing, this includes the latest versions of Chrome, Firefox, Edge, and Internet Explorer.) If your application depends on pointer events, we recommend using a third-party pointer events polyfill. We have opted not to include such a polyfill in React DOM, to avoid an increase in bundle size. +[Check out this example on CodeSandbox.](codesandbox://16-4-release-blog-post/pointer-events-example) + Huge thanks to [Philip Spiess](https://github.com/philipp-spiess) for contributing this change! ## Bugfix for `getDerivedStateFromProps` diff --git a/examples/16-4-release-blog-post/pointer-events-example.js b/examples/16-4-release-blog-post/pointer-events-example.js new file mode 100644 index 00000000000..23b9fb69d55 --- /dev/null +++ b/examples/16-4-release-blog-post/pointer-events-example.js @@ -0,0 +1,95 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +const CIRCLE_SIZE = 80; + +class DragBox extends React.Component { + state = { + hasCapture: false, + circleLeft: 80, + circleTop: 80, + }; + isDragging = false; + previousLeft = 0; + previousTop = 0; + + onDown = event => { + this.isDragging = true; + event.target.setPointerCapture(event.pointerId); + + // We store the initial coordinates to be able to calculate the changes + // later on. + this.extractPositionDelta(event); + }; + + onMove = event => { + if (!this.isDragging) { + return; + } + const {left, top} = this.extractPositionDelta(event); + + this.setState(({circleLeft, circleTop}) => ({ + circleLeft: circleLeft + left, + circleTop: circleTop + top, + })); + }; + + onUp = event => (this.isDragging = false); + onGotCapture = event => this.setState({hasCapture: true}); + onLostCapture = event => + this.setState({hasCapture: false}); + + extractPositionDelta = event => { + const left = event.pageX; + const top = event.pageY; + const delta = { + left: left - this.previousLeft, + top: top - this.previousTop, + }; + this.previousLeft = left; + this.previousTop = top; + return delta; + }; + + render() { + const {hasCapture, circleLeft, circleTop} = this.state; + + const boxStyle = { + border: '1px solid #d9d9d9', + margin: '10px 0 20px', + minHeight: 400, + width: '100%', + position: 'relative', + }; + + const circleStyle = { + width: CIRCLE_SIZE, + height: CIRCLE_SIZE, + borderRadius: CIRCLE_SIZE / 2, + position: 'absolute', + left: circleLeft, + top: circleTop, + backgroundColor: hasCapture ? 'blue' : 'green', + touchAction: 'none', + }; + + return ( +
+
+
+ ); + } +} + +ReactDOM.render( + , + document.getElementById('root') +); From 73006fefa196fae31dc358ac93b5c38e513d3981 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 16:47:27 -0700 Subject: [PATCH 12/17] Add an "oops!" so skimmers don't mistake the bad example for a good one --- content/blog/2018-05-23-react-v-16-4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index 7de02c51dc4..d41f63a2668 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -52,7 +52,7 @@ static getDerivedStateFromProps(props, state) { return { // Since this method fires on both props and state changes, local updates // to the controlled value will be ignored, since the props version always - // overrides it. + // overrides it. Oops! controlledValue: props.value, }; } From 728f74651e125ec51457454158cd5ecc61fd43e9 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 16:49:06 -0700 Subject: [PATCH 13/17] Sophie's edit to gDSFP section --- content/blog/2018-05-23-react-v-16-4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index d41f63a2668..113e7ec29ef 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -32,7 +32,7 @@ Huge thanks to [Philip Spiess](https://github.com/philipp-spiess) for contributi `getDerivedStateFromProps` is now called every time a component is rendered, regardless of the cause of the update. Previously, it was only called if the component was re-rendered by its parent, and would not fire as the result of a local `setState`. This was an oversight in the initial implementation that has now been corrected. The previous behavior was more similar to `componentWillReceiveProps`, but the improved behavior ensures compatibility with React's upcoming asynchronous rendering mode. -**Most likely, this doesn't require any changes to your components**. The rare cases where it does matter fall into one of two categories. +**This bug fix will not affect most apps**, but it may cause issues with a small fraction of components. The rare cases where it does matter fall into one of two categories: ### 1. Avoid Side Effects in `getDerivedStateFromProps` From 0fe6a7905e22430d3365d1b4d30656de6887f62d Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 17:11:46 -0700 Subject: [PATCH 14/17] More Sophie edits --- content/blog/2018-05-23-react-v-16-4.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index 113e7ec29ef..b07badc1f55 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -36,11 +36,9 @@ Huge thanks to [Philip Spiess](https://github.com/philipp-spiess) for contributi ### 1. Avoid Side Effects in `getDerivedStateFromProps` -Like the render method, `getDerivedStateFromProps` should be a pure function of props and state. You should move side-effectful code elsewhere. E.g. Flux dispatches typically belong inside the originating event handler. DOM mutations belong inside `componentDidMount` or `componentDidUpdate`. +Like the render method, `getDerivedStateFromProps` should be a pure function of props and state. Side effects in `getDerivedStateFromProps` were never supported, but since it now fires more often than it used to, the recent change may expose previously undiscovered bugs. -Side effects in `getDerivedStateFromProps` were already disallowed, but since it now fires more often than it used to, the recent change may expose previously undiscovered bugs. - -You can read more about this in our recent post about [preparing for asynchronous rendering](/blog/2018/03/27/update-on-async-rendering.html). +Side effectful code should be moved to other methods: for example, Flux dispatches typically belong inside the originating event handler, and manual DOM mutations belong inside componentDidMount or componentDidUpdate. You can read more about this in our recent post about [preparing for asynchronous rendering](/blog/2018/03/27/update-on-async-rendering.html). ### 2. Compare Incoming Props to Previous Props When Computing Controlled Values @@ -78,7 +76,7 @@ static getDerivedStateFromProps(props, state) { } ``` -However, try to avoid mixing props and state in this way. Prefer to have one source of truth for any value, and [lift state up](/docs/lifting-state-up.html) if necessary to share it between multiple components. +However, try to avoid mixing props and state in this way -- it's rare that state needs to duplicate a value that exists in props and doing so can lead to subtle bugs. Prefer to have one source of truth for any value, and [lift state up](/docs/lifting-state-up.html) if necessary to share it between multiple components. Most uses of `getDerivedStateFromProps` (and its predecessor `componentWillReceiveProps`) can be fixed by moving the state management to a parent component. Please remember that **most components do not need `getDerivedStateFromProps`**. It's not meant to serve as a one-to-one replacement for `componentWillReceiveProps`. We'll publish a follow-up post in the coming weeks with more recommendations on how to use (and not use) `getDerivedStateFromProps`. From 68dec67116152cfc2280928c44cd89d0441907cc Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 17:48:31 -0700 Subject: [PATCH 15/17] Remove pointer events example I'll add it back once CodeSandbox starts resolving `latest` to 16.4. --- content/blog/2018-05-23-react-v-16-4.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index b07badc1f55..b237d9d338a 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -24,8 +24,6 @@ The following event types are now available in React DOM: Please note that these events will only work in browsers that support the Pointer Events specification. (At the time of this writing, this includes the latest versions of Chrome, Firefox, Edge, and Internet Explorer.) If your application depends on pointer events, we recommend using a third-party pointer events polyfill. We have opted not to include such a polyfill in React DOM, to avoid an increase in bundle size. -[Check out this example on CodeSandbox.](codesandbox://16-4-release-blog-post/pointer-events-example) - Huge thanks to [Philip Spiess](https://github.com/philipp-spiess) for contributing this change! ## Bugfix for `getDerivedStateFromProps` From 30be0d05fa04b819dc4fb60eb422763dfcb0d478 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 17:55:32 -0700 Subject: [PATCH 16/17] Add back pointer events example --- content/blog/2018-05-23-react-v-16-4.md | 2 ++ examples/16-4-release-blog-post/pointer-events-example.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index b237d9d338a..b07badc1f55 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -24,6 +24,8 @@ The following event types are now available in React DOM: Please note that these events will only work in browsers that support the Pointer Events specification. (At the time of this writing, this includes the latest versions of Chrome, Firefox, Edge, and Internet Explorer.) If your application depends on pointer events, we recommend using a third-party pointer events polyfill. We have opted not to include such a polyfill in React DOM, to avoid an increase in bundle size. +[Check out this example on CodeSandbox.](codesandbox://16-4-release-blog-post/pointer-events-example) + Huge thanks to [Philip Spiess](https://github.com/philipp-spiess) for contributing this change! ## Bugfix for `getDerivedStateFromProps` diff --git a/examples/16-4-release-blog-post/pointer-events-example.js b/examples/16-4-release-blog-post/pointer-events-example.js index 23b9fb69d55..6c36070f5b6 100644 --- a/examples/16-4-release-blog-post/pointer-events-example.js +++ b/examples/16-4-release-blog-post/pointer-events-example.js @@ -1,7 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -const CIRCLE_SIZE = 80; +const CIRCLE_SIZE = 85; class DragBox extends React.Component { state = { From dc0ed4b4c6ad0be3c724d5dfab4dfb3726300acc Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 May 2018 17:57:26 -0700 Subject: [PATCH 17/17] since -> because --- content/blog/2018-05-23-react-v-16-4.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/2018-05-23-react-v-16-4.md b/content/blog/2018-05-23-react-v-16-4.md index b07badc1f55..4a2574056db 100644 --- a/content/blog/2018-05-23-react-v-16-4.md +++ b/content/blog/2018-05-23-react-v-16-4.md @@ -49,8 +49,8 @@ static getDerivedStateFromProps(props, state) { if (props.value !== state.controlledValue) { return { // Since this method fires on both props and state changes, local updates - // to the controlled value will be ignored, since the props version always - // overrides it. Oops! + // to the controlled value will be ignored, because the props version + // always overrides it. Oops! controlledValue: props.value, }; }