This commit is contained in:
Vincent Bailly 2019-02-01 13:52:21 +01:00
Родитель bb6a0cbd57
Коммит 57324467f4
5 изменённых файлов: 235 добавлений и 60 удалений

61
.gitignore поставляемый
Просмотреть файл

@ -1,61 +1,2 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
lib/

28
package.json Normal file
Просмотреть файл

@ -0,0 +1,28 @@
{
"name": "wastedrendersdetector",
"version": "1.0.0",
"description": "Tool to debug wasted renders",
"main": "lib/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"prepare": "yarn build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Microsoft/WastedRendersDetector.git"
},
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/Microsoft/WastedRendersDetector/issues"
},
"homepage": "https://github.com/Microsoft/WastedRendersDetector#readme",
"dependencies": {
"react": ">=16.0.0"
},
"devDependencies": {
"@types/react": ">=16.0.0",
"typescript": "^3.3.1"
}
}

117
src/index.tsx Normal file
Просмотреть файл

@ -0,0 +1,117 @@
import * as React from "react";
class WarningComponent extends React.Component<any> {
public render(): null {
return null;
}
public componentDidUpdate(prevProps: any): void {
if (!prevProps || !this.props) {
return;
}
// If some props where added or removed then the update is legit.
if (propsAddedOrRemoved(prevProps, this.props)) {
return;
}
const differences = deepSubstract(prevProps, this.props);
if (differences === null) {
console.error("🚨 WASTED RENDER 🚨, props changing reference for nothing: ", shallowSubstract(prevProps, this.props));
} else if (isOnlyFunctions(differences)) {
console.error("🚨 WASTED RENDER 🚨, props changed because of function reference(s) changing: ", differences);
}
}
}
function propsAddedOrRemoved(props1: any, props2: any): boolean {
const keysProps1 = Object.keys(props1);
const keysProps2 = Object.keys(props2);
return keysProps1.length !== keysProps2.length || keysProps1.some(k => keysProps2.indexOf(k) === -1);
}
/*
* Takes a difference object and check if it contains data or only functions.
*/
function isOnlyFunctions(a: any): boolean {
if (typeof a === "function") {
return true;
}
if (typeof a !== "object") {
return false;
}
return Object.keys(a).every(k => isOnlyFunctions(a[k]));
}
/*
* Returns "shallow" A - B
* Supported types: object
*/
function shallowSubstract(a: any, b: any): object | null {
const n: any = {};
Object.keys(a).forEach(key => {
if (a[key] !== b[key]) {
n[key] = a[key];
}
});
if (Object.keys(n).length) {
return n;
}
return null;
}
/*
* Returns "deep" A - B
* Supported types: any
* Not supported: circular references
*/
function deepSubstract(a: any, b: any): object | null {
if (a === b) {
return null;
}
if (typeof a !== "object" || typeof b !== "object") {
return a;
}
const n: any = {};
Object.keys(a).forEach(key => {
if (Object.keys(b).indexOf(key) === -1) {
n[key] = a[key];
} else {
const diff = deepSubstract(a[key], b[key]);
if (diff !== null) {
n[key] = diff;
}
}
});
if (Object.keys(n).length) {
return n;
}
return null;
}
/*
* HOC that checks that on every update whether the props update actually represents a real update.
* We do this by checking that props update represent a real update of data.
* We also check that the update is not solely due to a function changing reference.
* As a result, errors will be logged in the console in case of wasted renders.
* This component should only be used locally for troublshooting,
* no usage of it should be checked-in because using it is expensive.
*/
export function debugOnlyWastedRenderDetector<T>(Component: React.ComponentType<T>): React.ComponentType<T> {
return (props: T) => (
<React.Fragment>
<Component {...props} />
<WarningComponent {...props} />
</React.Fragment>
);
}

20
tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es2015",
"lib": ["es2015", "dom"],
"rootDir": "src",
"outDir": "lib",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"declarationMap": true,
"inlineSourceMap": true,
"inlineSources": false,
"jsx": "react",
"declaration": true,
"module": "esnext",
"moduleResolution": "node",
"preserveWatchOutput": true
}
}

69
yarn.lock Normal file
Просмотреть файл

@ -0,0 +1,69 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/prop-types@*":
version "15.5.8"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.8.tgz#8ae4e0ea205fe95c3901a5a1df7f66495e3a56ce"
integrity sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw==
"@types/react@>=16.0.0":
version "16.7.22"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.22.tgz#5bc6d166d5ac34b835756f0b736c7b1af0043e81"
integrity sha512-j/3tVoY09kHcTfbia4l67ofQn9xvktUvlC/4QN0KuBHAXlbU/wuGKMb8WfEb/vIcWxsOxHv559uYprkFDFfP8Q==
dependencies:
"@types/prop-types" "*"
csstype "^2.2.0"
csstype@^2.2.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.2.tgz#3043d5e065454579afc7478a18de41909c8a2f01"
integrity sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow==
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
loose-envify@^1.1.0, loose-envify@^1.3.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==
dependencies:
loose-envify "^1.3.1"
object-assign "^4.1.1"
react@>=16.0.0:
version "16.7.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.7.0.tgz#b674ec396b0a5715873b350446f7ea0802ab6381"
integrity sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.12.0"
scheduler@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b"
integrity sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
typescript@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b"
integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA==