Skip to content

Commit 25a7dd4

Browse files
authored
Merge pull request #1 from reactjs/polyfill-get-snapshot
Polyfill for getSnapshotBeforeUpdate
2 parents 78e214e + ff70f44 commit 25a7dd4

File tree

3 files changed

+248
-50
lines changed

3 files changed

+248
-50
lines changed

README.md

Lines changed: 18 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
## What is this project?
44

5-
React version 17 will deprecate several of the class component API lifecycles: `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (See [React RFC 6](https://github.com/reactjs/rfcs/pull/6) for more information about this decision.)
5+
React version 17 will deprecate several of the class component API lifecycles: `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Read the [Update on Async rendering blog post](https://deploy-preview-596--reactjs.netlify.com/blog/2018/03/15/update-on-async-rendering.html) to learn more about why.) A couple of new lifecycles are also being added to better support [async rendering mode](https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-16.html).
66

7-
This would typically require any third party libraries dependent on those lifecycles to release a new major version in order to adhere to semver. However, the `react-lifecycles-compat` polyfill offers a way to remain compatible with older versions of React (0.14.9+). 🎉😎
7+
Typically, this type of change would require third party libraries to release a new major version in order to adhere to semver. However, the `react-lifecycles-compat` polyfill offers a way to use the new lifecycles with older versions of React as well (0.14.9+) so no breaking release is required. This enables shared libraries to support both older and newer versions of React simultaneously.
88

99
## How can I use the polyfill
1010

@@ -17,58 +17,31 @@ yarn add react-lifecycles-compat
1717
npm install react-lifecycles-compat --save
1818
```
1919

20-
Next, update your component to use the new static lifecycle, `getDerivedStateFromProps`. For example:
21-
```js
22-
// Before
23-
class ExampleComponent extends React.Component {
24-
state = {
25-
derivedData: computeDerivedState(this.props)
26-
};
27-
28-
componentWillReceiveProps(nextProps) {
29-
if (this.props.someValue !== nextProps.someValue) {
30-
this.setState({
31-
derivedData: computeDerivedState(nextProps)
32-
});
33-
}
34-
}
35-
}
36-
37-
// After
38-
class ExampleComponent extends React.Component {
39-
// Initialize state in constructor,
40-
// Or with a property initializer.
41-
state = {};
42-
43-
static getDerivedStateFromProps(nextProps, prevState) {
44-
if (prevState.someMirroredValue !== nextProps.someValue) {
45-
return {
46-
derivedData: computeDerivedState(nextProps),
47-
someMirroredValue: nextProps.someValue
48-
};
49-
}
50-
51-
// Return null to indicate no change to state.
52-
return null;
53-
}
54-
}
55-
```
20+
Next, update your component and replace any of the deprecated lifecycles with new ones introduced with React 16.3. (Refer to the React docs for [examples of how to use the new lifecycles](https://deploy-preview-596--reactjs.netlify.com/blog/2018/03/15/update-on-async-rendering.html).)
5621

57-
Lastly, use the polyfill to make your component backwards compatible with older versions of React:
22+
Lastly, use the polyfill to make the new lifecycles work with older versions of React:
5823
```js
5924
import React from 'react';
6025
import polyfill from 'react-lifecycles-compat';
6126

6227
class ExampleComponent extends React.Component {
63-
static getDerivedStateFromProps(nextProps, prevState) {
64-
// ...
65-
}
66-
6728
// ...
6829
}
6930

70-
// Polyfill your component to work with older versions of React:
31+
// Polyfill your component so the new lifecycles will work with older versions of React:
7132
polyfill(ExampleComponent);
7233

7334
export default ExampleComponent;
74-
```
35+
```
36+
37+
## Which lifecycles are supported?
38+
39+
Currently, this polyfill supports [static `getDerivedStateFromProps`](https://deploy-preview-587--reactjs.netlify.com/docs/react-component.html#static-getderivedstatefromprops) and [`getSnapshotBeforeUpdate`](https://deploy-preview-587--reactjs.netlify.com/docs/react-component.html#getsnapshotbeforeupdate)- both introduced in version 16.3.
40+
41+
## Validation
42+
43+
Note that in order for the polyfill to work, none of the following lifecycles can be defined by your component: `componentWillMount`, `componentWillReceiveProps`, or `componentWillUpdate`.
44+
45+
Note also that if your component contains `getSnapshotBeforeUpdate`, `componentDidUpdate` must be defined as well.
46+
47+
An error will be thrown if any of the above conditions are not met.

index.js

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,27 @@ function componentWillReceiveProps(nextProps) {
2626
}
2727
}
2828

29+
function componentWillUpdate(nextProps, nextState) {
30+
try {
31+
var prevProps = this.props;
32+
var prevState = this.state;
33+
this.props = nextProps;
34+
this.state = nextState;
35+
this.__reactInternalSnapshot = this.getSnapshotBeforeUpdate(
36+
prevProps,
37+
prevState
38+
);
39+
} finally {
40+
this.props = prevProps;
41+
this.state = prevState;
42+
}
43+
}
44+
2945
// React may warn about cWM/cWRP/cWU methods being deprecated.
3046
// Add a flag to suppress these warnings for this special case.
3147
componentWillMount.__suppressDeprecationWarning = true;
3248
componentWillReceiveProps.__suppressDeprecationWarning = true;
49+
componentWillUpdate.__suppressDeprecationWarning = true;
3350

3451
module.exports = function polyfill(Component) {
3552
if (!Component.prototype || !Component.prototype.isReactComponent) {
@@ -38,18 +55,49 @@ module.exports = function polyfill(Component) {
3855

3956
if (typeof Component.getDerivedStateFromProps === 'function') {
4057
if (typeof Component.prototype.componentWillMount === 'function') {
41-
throw new Error('Cannot polyfill if componentWillMount already exists');
58+
throw new Error(
59+
'Cannot polyfill getDerivedStateFromProps() for components that define componentWillMount()'
60+
);
4261
} else if (
4362
typeof Component.prototype.componentWillReceiveProps === 'function'
4463
) {
4564
throw new Error(
46-
'Cannot polyfill if componentWillReceiveProps already exists'
65+
'Cannot polyfill getDerivedStateFromProps() for components that define componentWillReceiveProps()'
4766
);
4867
}
4968

5069
Component.prototype.componentWillMount = componentWillMount;
5170
Component.prototype.componentWillReceiveProps = componentWillReceiveProps;
5271
}
5372

73+
if (typeof Component.prototype.getSnapshotBeforeUpdate === 'function') {
74+
if (typeof Component.prototype.componentWillUpdate === 'function') {
75+
throw new Error(
76+
'Cannot polyfill getSnapshotBeforeUpdate() for components that define componentWillUpdate()'
77+
);
78+
}
79+
if (typeof Component.prototype.componentDidUpdate !== 'function') {
80+
throw new Error(
81+
'Cannot polyfill getSnapshotBeforeUpdate() for components that do not define componentDidUpdate() on the prototype'
82+
);
83+
}
84+
85+
Component.prototype.componentWillUpdate = componentWillUpdate;
86+
87+
var componentDidUpdate = Component.prototype.componentDidUpdate;
88+
89+
Component.prototype.componentDidUpdate = function componentDidUpdatePolyfill(
90+
prevProps,
91+
prevState
92+
) {
93+
componentDidUpdate.call(
94+
this,
95+
prevProps,
96+
prevState,
97+
this.__reactInternalSnapshot
98+
);
99+
};
100+
}
101+
54102
return Component;
55103
};

0 commit comments

Comments
 (0)