From e25d47070be0f12c870af39613a47bb53ef0e382 Mon Sep 17 00:00:00 2001 From: Ayc0 Date: Tue, 21 May 2024 16:07:02 +0200 Subject: [PATCH 1/7] redo logs --- src/components/page-react-timings/App.tsx | 145 +++++++++------------ src/components/page-react-timings/tasks.ts | 96 ++++++++++++++ 2 files changed, 154 insertions(+), 87 deletions(-) create mode 100644 src/components/page-react-timings/tasks.ts 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); +} -- 2.51.2 From 63eb3d2adc52fd9889df09af45f06f3ae73842b7 Mon Sep 17 00:00:00 2001 From: Ayc0 Date: Tue, 21 May 2024 16:07:46 +0200 Subject: [PATCH 2/7] fix getSnapshotBeforeUpdate --- src/components/page-react-timings/App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/page-react-timings/App.tsx b/src/components/page-react-timings/App.tsx index 42c1e8a..59add50 100644 --- a/src/components/page-react-timings/App.tsx +++ b/src/components/page-react-timings/App.tsx @@ -133,6 +133,7 @@ class ClassWithMetrics extends React.Component<{ override getSnapshotBeforeUpdate() { this.boundLog("getSnapshotBeforeUpdate"); + return ""; } override componentWillUnmount() { -- 2.51.2 From ae02ffdffdba3616ce9bf6751b16a66f5f8affa0 Mon Sep 17 00:00:00 2001 From: Ayc0 Date: Tue, 21 May 2024 16:22:18 +0200 Subject: [PATCH 3/7] add re-renders --- src/components/page-react-timings/App.tsx | 29 ++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/components/page-react-timings/App.tsx b/src/components/page-react-timings/App.tsx index 59add50..0b8a5b5 100644 --- a/src/components/page-react-timings/App.tsx +++ b/src/components/page-react-timings/App.tsx @@ -349,10 +349,16 @@ const examples = { function: ExampleWithFunction, }; +function resetConsole() { + console.clear(); + i = 65; +} + export default function App() { const [example, setExample] = React.useState( null ); + const [, forRerender] = React.useState({}); const Example: (() => JSX.Element) | undefined = examples[example!]; const [withTimings, setTiming] = React.useState(false); @@ -369,8 +375,7 @@ export default function App() { + {Object.keys(examples).map((example) => (
- } - > -
- {this.props.children} -
- - ); - } -} - -function ErrorThrower() { - const [hasError, setError] = React.useState(false); - if (hasError) { - throw new Error("oops"); - } - return ( - setError(true)} title="throw error"> - 💥 - - ); -} - -function Suspenser() { - const [hasSuspense, setSuspense] = React.useState(false); - if (hasSuspense) { - throw new Promise((res) => {}); - } - return ( - setSuspense(true)} title="trigger infinite suspense"> - 🚧 - - ); -} - -class ClassWithMetrics extends React.Component<{ - children?: React.ReactNode; - dashed?: boolean; -}> { - static override contextType = TimingContext; - declare context: React.ContextType; - - color = getRandomColor(); - componentId = String.fromCharCode(i++); - - boundLog(message: string) { - log( - `%cComponent ${this.componentId}: ${message}`, - `color: ${this.color}`, - this.context - ); - } - - constructor(props: { children?: React.ReactNode; dashed?: boolean }) { - super(props); - - this.boundLog("constructor"); - } - - override componentWillMount() { - this.boundLog("componentWillMount"); - } - - override componentWillReceiveProps() { - this.boundLog("componentWillReceiveProps"); - } - - override componentWillUpdate() { - this.boundLog("componentWillUpdate"); - } - - override componentDidMount() { - this.boundLog("componentDidMount"); - } - - override componentDidUpdate() { - this.boundLog("componentDidUpdate"); - } - - override shouldComponentUpdate() { - this.boundLog("shouldComponentUpdate"); - return true; - } - - override getSnapshotBeforeUpdate() { - this.boundLog("getSnapshotBeforeUpdate"); - return ""; - } - - override componentWillUnmount() { - this.boundLog("componentWillUnmount"); - } - - override render() { - this.boundLog("render"); - return ( -
-
- {this.componentId} - this.forceUpdate()} title="re-render"> - 🔄 - - - -
-
- {this.props.children} -
-
- ); - } -} - -class ClassWithChildrenWithMetrics extends ClassWithMetrics { - override render() { - this.boundLog("render"); - return ( -
-
- {this.componentId}* - this.forceUpdate()} title="re-render"> - 🔄 - - - -
-
- - - -
-
- ); - } -} - -function ExampleWithClass() { - return ( - - - - - - - - - - - ); -} - -const useMetrics = () => { - const color = React.useMemo(() => getRandomColor(), []); - const componentId = React.useMemo(() => String.fromCharCode(i++), []); - const withTiming = React.useContext(TimingContext); - - const boundLog = React.useCallback( - (message: string) => { - log( - `%cComponent ${componentId}: ${message}`, - `color: ${color}`, - withTiming - ); - }, - [withTiming] - ); - - boundLog("render"); - const [s, forceUpdate] = React.useState({}); - - React.useMemo(function useMemo() { - boundLog("useMemo"); - }, []); - - React.useInsertionEffect( - function useInsertionEffect() { - boundLog("useInsertionEffect"); - return function useInsertionEffect_cleanup() { - boundLog("useInsertionEffect cleanup"); - }; - }, - [s] - ); - - React.useLayoutEffect( - function useLayoutEffect() { - boundLog("useLayoutEffect"); - return function useLayoutEffect_cleanup() { - boundLog("useLayoutEffect cleanup"); - }; - }, - [s] - ); - - React.useEffect( - function useEffect() { - boundLog("useEffect"); - return function useEffect_cleanup() { - boundLog("useEffect cleanup"); - }; - }, - [s] - ); - - return { log: boundLog, color, componentId, forceUpdate }; -}; - -function FunctionWithMetrics({ - children, - dashed, -}: { - children?: React.ReactNode; - dashed?: boolean; -}) { - const { log, color, componentId, forceUpdate } = useMetrics(); - - return ( -
-
- {componentId} - forceUpdate({})} title="re-render"> - 🔄 - - - -
-
{children}
-
- ); -} - -function FunctionWithChildrenWithMetrics( - this: unknown, - { - children, - }: { - children?: React.ReactNode; - } -) { - const { log, color, componentId, forceUpdate } = useMetrics(); - - return ( -
-
- {componentId}* - forceUpdate({})} title="re-render"> - 🔄 - - - -
-
- - - -
-
- ); -} - -function ExampleWithFunction() { - return ( - - - - - - - - - - - ); -} - -const examples = { - class: ExampleWithClass, - function: ExampleWithFunction, -}; - -function resetConsole() { - console.clear(); - i = 65; -} - -export default function App() { - const [example, setExample] = React.useState( - null - ); - const [, forRerender] = React.useState({}); - const Example: (() => JSX.Element) | undefined = examples[example!]; - - const [withTimings, setTiming] = React.useState(false); - const [withTasks, setTasks] = React.useState(false); - const contextValue = React.useMemo( - () => ({ timings: withTimings, tasks: withTasks }), - [withTimings, withTasks] - ); +import { LifeCycleTimings } from "./LifeCycleTimings"; +export function App() { return ( -
-
- Timing {withTimings ? "enabled " : "disabled "} - -
-
- Tasks {withTasks ? "enabled " : "disabled "} - -
-
- Pick example: - - - {Object.keys(examples).map((example) => ( - - ))} -
- - - {Example && ( - - )} - - -
+ <> + + ); } diff --git a/src/components/page-react-timings/LifeCycleTimings.tsx b/src/components/page-react-timings/LifeCycleTimings.tsx new file mode 100644 index 0000000..de4ed87 --- /dev/null +++ b/src/components/page-react-timings/LifeCycleTimings.tsx @@ -0,0 +1,487 @@ +import React from "react"; +import { log } from "./tasks"; + +const getRandomColor = () => { + return `lch(${Math.floor(Math.random() * 20) + 50}% ${ + Math.floor(Math.random() * 50) + 50 + } ${Math.floor(Math.random() * 360)})`; +}; + +let i = 65; + +const TimingContext = React.createContext({ + tasks: false, + timings: false, +}); + +class ErrorBoundary extends React.Component< + { children?: React.ReactNode }, + { error: boolean } +> { + override state = { + error: false, + }; + + static getDerivedStateFromError() { + return { + error: true, + }; + } + + override render() { + if (this.state.error) { + return
Oops
; + } + + return ( + ⏸️ + } + > +
+ {this.props.children} +
+
+ ); + } +} + +function ErrorThrower() { + const [hasError, setError] = React.useState(false); + if (hasError) { + throw new Error("oops"); + } + return ( + setError(true)} title="throw error"> + 💥 + + ); +} + +function Suspenser() { + const [hasSuspense, setSuspense] = React.useState(false); + if (hasSuspense) { + throw new Promise((res) => {}); + } + return ( + setSuspense(true)} title="trigger infinite suspense"> + 🚧 + + ); +} + +function Legend() { + const style = { + width: "2ch", + display: "inline-block", + height: "0.5ex", + }; + return ( +
+
+ {" "} + Component +
+
+ Error + + Suspense boundary +
+
+ Error was + thrown & caught +
+
+ Suspense in + progress +
+
+ ); +} + +class ClassWithMetrics extends React.Component<{ + children?: React.ReactNode; +}> { + static override contextType = TimingContext; + declare context: React.ContextType; + + color = getRandomColor(); + componentId = String.fromCharCode(i++); + + boundLog(message: string) { + log( + `%cComponent ${this.componentId}: ${message}`, + `color: ${this.color}`, + this.context + ); + } + + constructor(props: { children?: React.ReactNode }) { + super(props); + + console.log(this.context); + + this.boundLog("constructor"); + } + + override componentWillMount() { + this.boundLog("componentWillMount"); + } + + override componentWillReceiveProps() { + this.boundLog("componentWillReceiveProps"); + } + + override componentWillUpdate() { + this.boundLog("componentWillUpdate"); + } + + override componentDidMount() { + this.boundLog("componentDidMount"); + } + + override componentDidUpdate() { + this.boundLog("componentDidUpdate"); + } + + override shouldComponentUpdate() { + this.boundLog("shouldComponentUpdate"); + return true; + } + + override getSnapshotBeforeUpdate() { + this.boundLog("getSnapshotBeforeUpdate"); + return ""; + } + + override componentWillUnmount() { + this.boundLog("componentWillUnmount"); + } + + override render() { + this.boundLog("render"); + return ( +
+
+ {this.componentId} + { + resetConsole(); + this.forceUpdate(); + }} + title="re-render" + > + 🔄 + + + +
+
+ {this.props.children} +
+
+ ); + } +} + +class ClassWithChildrenWithMetrics extends ClassWithMetrics { + override render() { + this.boundLog("render"); + return ( +
+
+ {this.componentId}* + { + resetConsole(); + this.forceUpdate(); + }} + title="re-render" + > + 🔄 + + + +
+
+ + + +
+
+ ); + } +} + +function ExampleWithClass() { + return ( + <> + + + + + + + + + + + + + ); +} + +const useMetrics = () => { + const color = React.useMemo(() => getRandomColor(), []); + const componentId = React.useMemo(() => String.fromCharCode(i++), []); + const withTiming = React.useContext(TimingContext); + const elementRef = React.useRef(null); + + const boundLog = React.useCallback( + (message: string) => { + log( + `%cComponent ${componentId}: ${message}`, + `color: ${color}`, + withTiming + ); + }, + [withTiming] + ); + + boundLog("render"); + const [s, forceUpdate] = React.useState({}); + + React.useMemo(function useMemo() { + boundLog("useMemo"); + }, []); + + React.useInsertionEffect( + function useInsertionEffect() { + boundLog("useInsertionEffect " + (elementRef.current ? "node" : "null")); + return function useInsertionEffect_cleanup() { + boundLog( + "useInsertionEffect cleanup " + (elementRef.current ? "node" : "null") + ); + }; + }, + [s] + ); + + React.useLayoutEffect( + function useLayoutEffect() { + boundLog("useLayoutEffect " + (elementRef.current ? "node" : "null")); + return function useLayoutEffect_cleanup() { + boundLog( + "useLayoutEffect cleanup " + (elementRef.current ? "node" : "null") + ); + }; + }, + [s] + ); + + React.useEffect( + function useEffect() { + boundLog("useEffect " + (elementRef.current ? "node" : "null")); + return function useEffect_cleanup() { + boundLog("useEffect cleanup " + (elementRef.current ? "node" : "null")); + }; + }, + [s] + ); + + return { + log: boundLog, + color, + componentId, + forceUpdate, + elementRef: elementRef, + }; +}; + +function FunctionWithMetrics({ children }: { children?: React.ReactNode }) { + const { log, color, componentId, forceUpdate, elementRef } = useMetrics(); + + return ( +
+
+ {componentId} + { + resetConsole(); + forceUpdate({}); + }} + title="re-render" + > + 🔄 + + + +
+
{children}
+
+ ); +} + +function FunctionWithChildrenWithMetrics(this: unknown) { + const { log, color, componentId, forceUpdate, elementRef } = useMetrics(); + + return ( +
+
+ {componentId}* + { + resetConsole(); + forceUpdate({}); + }} + title="re-render" + > + 🔄 + + + +
+
+ + + +
+
+ ); +} + +function ExampleWithFunction() { + return ( + <> + + + + + + + + + + + + + ); +} + +const examples = { + class: ExampleWithClass, + function: ExampleWithFunction, +}; + +function resetConsole() { + console.clear(); + i = 65; +} + +export function LifeCycleTimings() { + const [example, setExample] = React.useState( + null + ); + const [, forceUpdate] = React.useState({}); + const Example: (() => JSX.Element) | undefined = examples[example!]; + + const [withTimings, setTiming] = React.useState(false); + const [withTasks, setTasks] = React.useState(false); + const contextValue = React.useMemo( + () => ({ timings: withTimings, tasks: withTasks }), + [withTimings, withTasks] + ); + + return ( +
+
+ Timing {withTimings ? "enabled " : "disabled "} + +
+
+ Tasks {withTasks ? "enabled " : "disabled "} + +
+
+ Pick example: + + + {Object.keys(examples).map((example) => ( + + ))} +
+ + + {Example && ( + + )} + + +
+ ); +} diff --git a/src/components/page-react-timings/index.tsx b/src/components/page-react-timings/index.tsx index f69004e..1b7f77d 100644 --- a/src/components/page-react-timings/index.tsx +++ b/src/components/page-react-timings/index.tsx @@ -3,8 +3,8 @@ import React from "react"; import { render } from "react-dom"; import { createRoot } from "react-dom/client"; -import App from "./App.tsx"; -import Switcher, { isLegacyRoot } from "./Switcher.tsx"; +import { App } from "./App"; +import Switcher, { isLegacyRoot } from "./Switcher"; const rootElement = document.getElementById("root")!; diff --git a/src/components/page-react-timings/tasks.ts b/src/components/page-react-timings/tasks.ts index 98a0e07..786a8ec 100644 --- a/src/components/page-react-timings/tasks.ts +++ b/src/components/page-react-timings/tasks.ts @@ -1,3 +1,14 @@ +declare global { + const scheduler: { + postTask: ( + task: Function, + opts: { + priority: "user-blocking" | "user-visible" | "background"; + } + ) => void; + }; +} + const div = document.createElement("div"); document.body.appendChild(div); -- 2.51.2 From f6f6552080e237119873466630f1d22226f04552 Mon Sep 17 00:00:00 2001 From: Ayc0 Date: Tue, 21 May 2024 18:37:44 +0200 Subject: [PATCH 6/7] layout --- .../page-react-timings/LifeCycleTimings.tsx | 170 ++++++++++-------- src/pages/react-timings.astro | 16 +- 2 files changed, 105 insertions(+), 81 deletions(-) diff --git a/src/components/page-react-timings/LifeCycleTimings.tsx b/src/components/page-react-timings/LifeCycleTimings.tsx index de4ed87..aaa4acb 100644 --- a/src/components/page-react-timings/LifeCycleTimings.tsx +++ b/src/components/page-react-timings/LifeCycleTimings.tsx @@ -78,9 +78,11 @@ function Legend() { height: "0.5ex", }; return ( -
+ <>
- {" "} + {" "} Component
@@ -95,7 +97,7 @@ function Legend() { Suspense in progress
-
+ ); } @@ -227,19 +229,16 @@ class ClassWithChildrenWithMetrics extends ClassWithMetrics { function ExampleWithClass() { return ( - <> - - - - - - - - - - - - + + + + + + + + + + ); } @@ -376,19 +375,16 @@ function FunctionWithChildrenWithMetrics(this: unknown) { function ExampleWithFunction() { return ( - <> - - - - - - - - - - - - + + + + + + + + + + ); } @@ -417,49 +413,9 @@ export function LifeCycleTimings() { ); return ( -
-
- Timing {withTimings ? "enabled " : "disabled "} - -
-
- Tasks {withTasks ? "enabled " : "disabled "} - -
-
+ <> +
Pick example: - - {Object.keys(examples).map((example) => (
- - - {Example && ( - - )} - - -
+ + {Example && ( + <> +
+ + +
+ +
+ Timing {withTimings ? "enabled " : "disabled "} + +
+ +
+ Tasks {withTasks ? "enabled " : "disabled "} + +
+ +
+ + + + + +
+ +
+ +
+ + )} + ); } diff --git a/src/pages/react-timings.astro b/src/pages/react-timings.astro index 7d4d181..1eff72e 100644 --- a/src/pages/react-timings.astro +++ b/src/pages/react-timings.astro @@ -13,14 +13,26 @@ import Page from '@layouts/Page.astro'; -- 2.51.2