first commit
This commit is contained in:
Родитель
bb6a0cbd57
Коммит
57324467f4
|
@ -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/
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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==
|
Загрузка…
Ссылка в новой задаче