diff --git a/src/components/page-react-timings/App.tsx b/src/components/page-react-timings/App.tsx
index ba5eaaa..42c1e8a 100644
--- a/src/components/page-react-timings/App.tsx
+++ b/src/components/page-react-timings/App.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import { log } from "./tasks";
const getRandomColor = () => {
return `lch(${Math.floor(Math.random() * 20) + 50}% ${
@@ -19,60 +20,10 @@ declare global {
};
}
-const TimingContext = React.createContext(false);
-
-function measureTiming(label: string, color: string, withTiming: boolean) {
- if (withTiming) {
- console.log(performance.now().toFixed(1) + "ms " + label, color);
- queueMicrotask(function measureTiming_microTask() {
- console.log(
- performance.now().toFixed(1) + "ms microtask " + label,
- color
- );
- });
- requestAnimationFrame(function measureTiming_requestAnimationFrame() {
- console.log(
- performance.now().toFixed(1) + "ms requestAnimationFrame " + label,
- color
- );
- });
- scheduler.postTask(
- function measureTiming_userBlockingTask() {
- console.log(
- performance.now().toFixed(1) + "ms user-blocking task " + label,
- color
- );
- },
- {
- priority: "user-blocking",
- }
- );
- scheduler.postTask(
- function measureTiming_userVisibleTask() {
- console.log(
- performance.now().toFixed(1) + "ms user-visible task " + label,
- color
- );
- },
- {
- priority: "user-visible",
- }
- );
- scheduler.postTask(
- function measureTiming_backgroundTask() {
- console.log(
- performance.now().toFixed(1) + "ms background task " + label,
- color
- );
- },
- {
- priority: "background",
- }
- );
- } else {
- console.log(label, color);
- }
-}
+const TimingContext = React.createContext({
+ tasks: false,
+ timings: false,
+});
class ErrorBoundary extends React.Component<
{ children?: React.ReactNode },
@@ -141,8 +92,8 @@ class ClassWithMetrics extends React.Component<{
color = getRandomColor();
componentId = String.fromCharCode(i++);
- log(message: string) {
- measureTiming(
+ boundLog(message: string) {
+ log(
`%cComponent ${this.componentId}: ${message}`,
`color: ${this.color}`,
this.context
@@ -152,48 +103,48 @@ class ClassWithMetrics extends React.Component<{
constructor(props: { children?: React.ReactNode; dashed?: boolean }) {
super(props);
- this.log("constructor");
+ this.boundLog("constructor");
}
override componentWillMount() {
- this.log("componentWillMount");
+ this.boundLog("componentWillMount");
}
override componentWillReceiveProps() {
- this.log("componentWillReceiveProps");
+ this.boundLog("componentWillReceiveProps");
}
override componentWillUpdate() {
- this.log("componentWillUpdate");
+ this.boundLog("componentWillUpdate");
}
override componentDidMount() {
- this.log("componentDidMount");
+ this.boundLog("componentDidMount");
}
override componentDidUpdate() {
- this.log("componentDidUpdate");
+ this.boundLog("componentDidUpdate");
}
override shouldComponentUpdate() {
- this.log("shouldComponentUpdate");
+ this.boundLog("shouldComponentUpdate");
return true;
}
override getSnapshotBeforeUpdate() {
- this.log("getSnapshotBeforeUpdate");
+ this.boundLog("getSnapshotBeforeUpdate");
}
override componentWillUnmount() {
- this.log("componentWillUnmount");
+ this.boundLog("componentWillUnmount");
}
override render() {
- this.log("render");
+ this.boundLog("render");
return (
@@ -263,9 +214,9 @@ const useMetrics = () => {
const componentId = React.useMemo(() => String.fromCharCode(i++), []);
const withTiming = React.useContext(TimingContext);
- const log = React.useCallback(
+ const boundLog = React.useCallback(
(message: string) => {
- measureTiming(
+ log(
`%cComponent ${componentId}: ${message}`,
`color: ${color}`,
withTiming
@@ -274,18 +225,18 @@ const useMetrics = () => {
[withTiming]
);
- log("render");
+ boundLog("render");
const [s, forceUpdate] = React.useState({});
React.useMemo(function useMemo() {
- log("useMemo");
+ boundLog("useMemo");
}, []);
React.useInsertionEffect(
function useInsertionEffect() {
- log("useInsertionEffect");
+ boundLog("useInsertionEffect");
return function useInsertionEffect_cleanup() {
- log("useInsertionEffect cleanup");
+ boundLog("useInsertionEffect cleanup");
};
},
[s]
@@ -293,9 +244,9 @@ const useMetrics = () => {
React.useLayoutEffect(
function useLayoutEffect() {
- log("useLayoutEffect");
+ boundLog("useLayoutEffect");
return function useLayoutEffect_cleanup() {
- log("useLayoutEffect cleanup");
+ boundLog("useLayoutEffect cleanup");
};
},
[s]
@@ -303,15 +254,15 @@ const useMetrics = () => {
React.useEffect(
function useEffect() {
- log("useEffect");
+ boundLog("useEffect");
return function useEffect_cleanup() {
- log("useEffect cleanup");
+ boundLog("useEffect cleanup");
};
},
[s]
);
- return { log, color, componentId, forceUpdate };
+ return { log: boundLog, color, componentId, forceUpdate };
};
function FunctionWithMetrics({
@@ -403,21 +354,39 @@ export default function App() {
);
const Example: (() => JSX.Element) | undefined = examples[example!];
- const [withTiming, setTiming] = React.useState(false);
+ const [withTimings, setTiming] = React.useState(false);
+ const [withTasks, setTasks] = React.useState(false);
+ const contextValue = React.useMemo(
+ () => ({ timings: withTimings, tasks: withTasks }),
+ [withTimings, withTasks]
+ );
return (
- Timing {withTiming ? "enabled " : "disabled "}
+ Timing {withTimings ? "enabled " : "disabled "}
+
+
+
+ Tasks {withTasks ? "enabled " : "disabled "}
@@ -444,9 +413,11 @@ export default function App() {
))}
-
+
- {Example && }
+ {Example && (
+
+ )}
diff --git a/src/components/page-react-timings/tasks.ts b/src/components/page-react-timings/tasks.ts
new file mode 100644
index 0000000..98a0e07
--- /dev/null
+++ b/src/components/page-react-timings/tasks.ts
@@ -0,0 +1,96 @@
+const div = document.createElement("div");
+document.body.appendChild(div);
+
+export function syncTask(cb: (type: string) => void) {
+ (function syncCallback() {
+ cb("sync");
+ })();
+}
+export function microTask(cb: (type: string) => void) {
+ queueMicrotask(function microTaskCallback() {
+ cb("micro task");
+ });
+}
+export function userBlockingTask(cb: (type: string) => void) {
+ scheduler.postTask(
+ function userBlockingTaskCallback() {
+ cb("task (user-blocking)");
+ },
+ { priority: "user-blocking" }
+ );
+}
+export function userVisibleTask(cb: (type: string) => void) {
+ scheduler.postTask(
+ function userVisibleTaskCallback() {
+ cb("task (user-visible)");
+ },
+ { priority: "user-visible" }
+ );
+}
+export function backgroundTask(cb: (type: string) => void) {
+ scheduler.postTask(
+ function backgroundTaskCallback() {
+ cb("task (background)");
+ },
+ { priority: "background" }
+ );
+}
+export function animationTask(cb: (type: string) => void) {
+ requestAnimationFrame(function animationTaskCallback() {
+ cb("animation frame");
+ });
+}
+
+export function startAllTasks(cb: (type: string) => void) {
+ syncTask(cb);
+ microTask(cb);
+ userBlockingTask(cb);
+ userVisibleTask(cb);
+ backgroundTask(cb);
+ animationTask(cb);
+}
+
+function getNow(timings: boolean) {
+ if (!timings) {
+ return "";
+ }
+
+ return (
+ "Now: " +
+ performance.now().toFixed(1) +
+ "ms | Timeline: " +
+ document.timeline.currentTime +
+ "ms | "
+ );
+}
+
+export function log(
+ label: string,
+ color: string,
+ {
+ tasks,
+ timings = false,
+ }: {
+ tasks?: boolean;
+ timings?: boolean;
+ } = {}
+) {
+ if (!tasks) {
+ console.log(getNow(timings) + label, color);
+ return;
+ }
+
+ const timingStyle = [
+ "font-style: italic; text-decoration-line: underline",
+ "",
+ ];
+
+ const cb = (type: string) =>
+ console.log(
+ getNow(timings) + "%c" + type + "%c " + label,
+ ...timingStyle,
+ color
+ );
+
+ startAllTasks(cb);
+}