Skip to content

Commit c305dfc

Browse files
authored
Merge pull request #301 from welldone-software/react-19
React 19
2 parents e243f07 + cad504a commit c305dfc

File tree

99 files changed

+3104
-3244
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+3104
-3244
lines changed

.eslintignore

Lines changed: 0 additions & 3 deletions
This file was deleted.

.eslintrc

Lines changed: 0 additions & 50 deletions
This file was deleted.

.github/workflows/main.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,6 @@ jobs:
1717
- name: Run Cypress tests
1818
run: yarn cypress:ci
1919

20-
cypress-tests-classic:
21-
runs-on: ubuntu-latest
22-
strategy:
23-
fail-fast: false
24-
steps:
25-
- uses: actions/checkout@v4
26-
- uses: ./.github/actions/setup
27-
- name: Run Cypress tests
28-
run: yarn cypress:ci:classic
29-
3020
unit-tests:
3121
runs-on: ubuntu-latest
3222
steps:

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"eslint.alwaysShowStatus": true,
1111
"eslint.format.enable": true,
1212
"eslint.codeActionsOnSave.mode": "problems",
13+
"editor.formatOnSave": true,
1314

1415
"flow.enabled": false,
1516

@@ -19,6 +20,7 @@
1920

2021
"jestrunner.debugOptions": {"args": ["--watch"]},
2122
"jestrunner.configPath": "jest.config.js",
23+
2224
"cSpell.words": [
2325
"astring",
2426
"lcov",

README.md

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,36 @@
1212

1313
`why-did-you-render` by [Welldone Software](https://welldone.software/) monkey patches **`React`** to notify you about potentially avoidable re-renders. (Works with **`React Native`** as well.)
1414

15-
For example, if you pass `style={{width: '100%'}}` to a big pure component it would always re-render on every element creation:
15+
For example, if you pass `style={{width: '100%'}}` to a big memo component it would always re-render on every element creation:
1616
```jsx
17-
<BigListPureComponent style={{width: '100%'}}/>
17+
<MemoBigList style={{width: '100%'}}/>
1818
```
19-
2019
It can also help you to simply track when and why a certain component re-renders.
2120

21+
> [!CAUTION]
22+
> The library was not tested with [React Compiler](https://react.dev/learn/react-compiler) at all. I believe it's completely incompatible with it.
23+
24+
> [!CAUTION]
25+
> Not all re-renders are *"bad"*. Sometimes shenanigan to reduce re-renders can either hurt your App's performance or have a neglagable effect, in which case it would be just a waste of your efforts, and complicate your code. Try to focus on heavier components when optimizing and use the [React profiler](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) inside the React dev-tools to measure the effects of any changes.
26+
27+
> [!NOTE]
28+
I've joined the React team, specifically working on React tooling. This role has opened up exciting opportunities to enhance the developer experience for React users— and your input could offer valuable insights to help me with this effort. Please join the conversation in the [discussion thread](https://github.com/welldone-software/why-did-you-render/discussions/309)!
29+
2230
## Setup
23-
The latest version of the library was tested [(unit tests and E2E)]((https://travis-ci.com/welldone-software/why-did-you-render.svg?branch=master)) with **`React@18`** only. For React 17 and 16, please use version @^7.
31+
The latest version of the library was tested [(unit tests and E2E)]((https://travis-ci.com/welldone-software/why-did-you-render.svg?branch=master)) with **`React@19`** only.
32+
* [For `React 18`, please see the readme for version @^8](https://github.com/welldone-software/why-did-you-render/tree/version-8).
33+
* [For `React 17` and `React 16`, please see the readme for version @^7](https://github.com/welldone-software/why-did-you-render/tree/version-7).
2434

2535
```
2636
npm install @welldone-software/why-did-you-render --save-dev
2737
```
2838
or
2939
```
30-
yarn add --dev @welldone-software/why-did-you-render
40+
yarn add @welldone-software/why-did-you-render -D
3141
```
42+
Set the library to be the React's importSource and make sure `preset-react` is in `development` mode.
3243

33-
If you use the `automatic` JSX transformation, set the library to be the import source, and make sure `preset-react` is in `development` mode.
44+
This is because `React 19` requires using the `automatic` [JSX transformation](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html).
3445
```js
3546
['@babel/preset-react', {
3647
runtime: 'automatic',
@@ -43,15 +54,19 @@ If you use the `automatic` JSX transformation, set the library to be the import
4354

4455
#### Bare workflow
4556

46-
Unfortunately, the `metro-react-native-babel-preset` that comes with react-native out of the box does not allow you to change the options of the `babel/plugin-transform-react-jsx` plugin. Just add the plugin with options as listed below and start react-native packager as usual. Default env for babel is "development". If you do not use expo when working with react-native, the following method will help you.
57+
Add the plugin as listed below and start react-native packager as usual. Default env for babel is "development". If you do not use expo when working with react-native, the following method will help you.
4758

4859
```js
4960
module.exports = {
5061
presets: ['module:metro-react-native-babel-preset'],
5162

5263
env: {
5364
development: {
54-
plugins: [['@babel/plugin-transform-react-jsx', { runtime: 'classic' }]],
65+
plugins: [['@babel/plugin-transform-react-jsx', {
66+
runtime: 'automatic',
67+
development: process.env.NODE_ENV === 'development',
68+
importSource: '@welldone-software/why-did-you-render',
69+
}]],
5570
},
5671
},
5772
}
@@ -78,10 +93,10 @@ module.exports = function (api) {
7893
};
7994
```
8095

81-
> Notice: Create React App (CRA) ^4 **does use the `automatic` JSX transformation.**
96+
> Notice: Create React App (CRA) ^4 **uses the `automatic` JSX transformation.**
8297
> [See the following comment on how to do this step with CRA](https://github.com/welldone-software/why-did-you-render/issues/154#issuecomment-773905769)
8398
84-
Create a `wdyr.js` file and import it as **the first import** in your application.
99+
Create a `wdyr.js` file and import it as **the very first import** in your application.
85100

86101
`wdyr.js`:
87102
```jsx
@@ -95,35 +110,36 @@ if (process.env.NODE_ENV === 'development') {
95110
}
96111
```
97112

98-
> **Notice: The library should *NEVER* be used in production because it slows down React**
113+
> [!CAUTION]
114+
> The library should *NEVER* be used in production because:
115+
> - It significantly slows down React
116+
> - It monkey patches React and can result in unexpected behavior
99117
100118
In [Typescript](https://github.com/welldone-software/why-did-you-render/issues/161), call the file wdyr.ts and add the following line to the top of the file to import the package's types:
101119
```tsx
102120
/// <reference types="@welldone-software/why-did-you-render" />
103121
```
104122

105-
Import `wdyr` as the first import (even before `react-hot-loader`):
123+
Import `wdyr` as the first import (even before `react-hot-loader` if you use it):
106124

107125
`index.js`:
126+
108127
```jsx
109128
import './wdyr'; // <--- first import
110129

111130
import 'react-hot-loader';
112-
import {hot} from 'react-hot-loader/root';
113131

114132
import React from 'react';
115133
import ReactDOM from 'react-dom';
116134
// ...
117135
import {App} from './app';
118136
// ...
119-
const HotApp = hot(App);
120-
// ...
121-
ReactDOM.render(<HotApp/>, document.getElementById('root'));
137+
ReactDOM.render(<App/>, document.getElementById('root'));
122138
```
123139

124-
If you use `trackAllPureComponents` like we suggest, all pure components ([React.PureComponent](https://reactjs.org/docs/react-api.html#reactpurecomponent) or [React.memo](https://reactjs.org/docs/react-api.html#reactmemo)) will be tracked.
140+
If you use `trackAllPureComponents`, all pure components ([React.PureComponent](https://reactjs.org/docs/react-api.html#reactpurecomponent) or [React.memo](https://reactjs.org/docs/react-api.html#reactmemo)) will be tracked.
125141

126-
Otherwise, add `whyDidYouRender = true` to component classes/functions you want to track. (f.e `Component.whyDidYouRender = true`)
142+
Otherwise, add `whyDidYouRender = true` to ad-hoc components to track them. (f.e `Component.whyDidYouRender = true`)
127143

128144
More information about what is tracked can be found in [Tracking Components](#tracking-components).
129145

@@ -241,7 +257,8 @@ Optionally you can pass in `options` as the second parameter. The following opti
241257
- `titleColor`
242258
- `diffNameColor`
243259
- `diffPathColor`
244-
- `notifier: ({Component, displayName, hookName, prevProps, prevState, prevHook, nextProps, nextState, nextHook, reason, options, ownerDataMap}) => void`
260+
- `textBackgroundColor`
261+
- `notifier: ({Component, displayName, hookName, prevProps, prevState, prevHookResult, nextProps, nextState, nextHookResult, reason, options, ownerDataMap}) => void`
245262
- `getAdditionalOwnerData: (element) => {...}`
246263

247264
#### include / exclude
@@ -283,7 +300,7 @@ whyDidYouRender(React, {
283300
});
284301
```
285302

286-
> There is currently a problem with rewriting exports of imported files in webpack. A workaround is available here: [#85 - trackExtraHooks cannot set property](https://github.com/welldone-software/why-did-you-render/issues/85)
303+
> This feature is rewriting exports of imported files. There is currently a problem with that approach in webpack. A workaround is available here: [#85 - trackExtraHooks cannot set property](https://github.com/welldone-software/why-did-you-render/issues/85)
287304
288305
#### logOwnerReasons
289306
##### (default: `true`)
@@ -325,10 +342,11 @@ If you don't want to use `console.group` to group logs you can print them as sim
325342

326343
Grouped logs can be collapsed.
327344

328-
#### titleColor / diffNameColor / diffPathColor
345+
#### titleColor / diffNameColor / diffPathColor / textBackgroundColor
329346
##### (default titleColor: `'#058'`)
330347
##### (default diffNameColor: `'blue'`)
331348
##### (default diffPathColor: `'red'`)
349+
##### (default textBackgroundColor: `'white`)
332350

333351
Controls the colors used in the console notifications
334352

babel.config.cjs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const compact = require('lodash/compact');
33
module.exports = function(api) {
44
const isProd = process.env.NODE_ENV === 'production';
55
const isTest = process.env.NODE_ENV === 'test';
6-
const isUseClassicJSX = process.env.USE_CLASSIC_JSX === 'true';
76

87
api.cache(false);
98

@@ -12,16 +11,15 @@ module.exports = function(api) {
1211
modules: isTest ? 'commonjs' : false,
1312
}],
1413
['@babel/preset-react', {
15-
runtime: isUseClassicJSX ? 'classic' : 'automatic',
14+
runtime: 'automatic',
1615
development: true,
17-
importSource: isUseClassicJSX ? undefined : `${__dirname}`,
16+
importSource: `${__dirname}`,
1817
}],
1918
];
2019

2120
const plugins = compact([
22-
(!isProd && !isTest) && 'react-hot-loader/babel',
23-
!isProd && '@babel/plugin-transform-class-properties',
21+
(!isProd && !isTest) && 'react-refresh/babel',
2422
]);
2523

26-
return { presets, plugins };
24+
return {presets, plugins};
2725
};

cypress/e2e/big_list.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ it('Big list basic example', () => {
33
cy.contains('button', 'Increase!').click();
44

55
expect(console.group).to.be.calledWithMatches([
6-
{ match: 'BigList', times: 1 },
7-
{ match: /props.*style\W/, times: 1 },
6+
{match: 'BigList', times: 1},
7+
{match: /props.*style\W/, times: 1},
88
]);
99

1010
expect(console.log).to.be.calledWithMatches([
11-
{ match: [() => true, 'Re-rendered because of props changes'], times: 1 },
11+
{match: [() => true, 'Re-rendered because of props changes'], times: 1},
1212
]);
1313
});
1414
});

cypress/e2e/child-of-pure-component.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ it('Child of Pure Component', () => {
66
cy.contains('button', 'clicks:').should('contain', '2');
77

88
expect(console.group).to.be.calledWithMatches([
9-
{ match: 'PureFather', times: 2 },
10-
{ match: /props.*children\W/, times: 2 },
9+
{match: 'PureFather', times: 2},
10+
{match: /props.*children\W/, times: 2},
1111
]);
1212

1313
expect(console.log).to.be.calledWithMatches([
14-
{ match: 'syntax always produces a *NEW* immutable React element', times: 2 },
14+
{match: 'syntax always produces a *NEW* immutable React element', times: 2},
1515
]);
1616
});
1717
});

cypress/e2e/clone-element.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
it('Creating react element using React.cloneElement', () => {
22
cy.visitAndSpyConsole('/#cloneElement', console => {
33
expect(console.group).to.be.calledWithMatches([
4-
{ match: 'TestComponent', times: 1 },
4+
{match: 'TestComponent', times: 1},
55
]);
66

77
expect(console.log).to.be.calledWithMatches([
8-
{ match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 1 },
8+
{match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 1},
99
]);
1010
});
1111
});

cypress/e2e/create-factory.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
it('Creating react element using React.createFactory', () => {
22
cy.visitAndSpyConsole('/#createFactory', console => {
33
expect(console.group).to.be.calledWithMatches([
4-
{ match: 'TestComponent', times: 1 },
4+
{match: 'TestComponent', times: 1},
55
]);
66

77
expect(console.log).to.be.calledWithMatches([
8-
{ match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 1 },
8+
{match: [() => true, 'Re-rendered because the props object itself changed but its values are all equal.'], times: 1},
99
]);
1010
});
1111
});

0 commit comments

Comments
 (0)