Skip to content

Commit 6e32713

Browse files
committed
Clean up #795
1 parent deff4ef commit 6e32713

File tree

4 files changed

+40
-42
lines changed

4 files changed

+40
-42
lines changed

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,10 +2037,10 @@ This functionality is powered by [patch-console](https://github.com/vadimdemedes
20372037

20382038
###### onRender
20392039

2040-
Type: `(renderTime: number) => void`\
2040+
Type: `({renderTime: number}) => void`\
20412041
Default: `undefined`
20422042

2043-
Runs the given callback after each render and re-render with a renderResult object.
2043+
Runs the given callback after each render and re-render with a metrics object.
20442044

20452045
###### debug
20462046

src/ink.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ import {accessibilityContext as AccessibilityContext} from './components/Accessi
2020

2121
const noop = () => {};
2222

23-
export type RenderResult = {
23+
/**
24+
Performance metrics for a render operation.
25+
*/
26+
export type RenderMetrics = {
27+
/**
28+
Time spent rendering in milliseconds.
29+
*/
2430
renderTime: number;
2531
};
2632

@@ -31,7 +37,7 @@ export type Options = {
3137
debug: boolean;
3238
exitOnCtrlC: boolean;
3339
patchConsole: boolean;
34-
onRender?: (renderTime: RenderResult) => void;
40+
onRender?: (metrics: RenderMetrics) => void;
3541
isScreenReaderEnabled?: boolean;
3642
waitUntilExit?: () => Promise<void>;
3743
maxFps?: number;
@@ -173,9 +179,8 @@ export default class Ink {
173179
this.rootNode,
174180
this.isScreenReaderEnabled,
175181
);
176-
if (this.options.onRender) {
177-
this.options.onRender({renderTime: performance.now() - startTime});
178-
}
182+
183+
this.options.onRender?.({renderTime: performance.now() - startTime});
179184

180185
// If <Static> output isn't empty, it means new children have been added to it
181186
const hasStaticOutput = staticOutput && staticOutput !== '\n';
@@ -287,17 +292,12 @@ export default class Ink {
287292
</AccessibilityContext.Provider>
288293
);
289294

290-
const start = performance.now();
291295
// @ts-expect-error the types for `react-reconciler` are not up to date with the library.
292296
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
293297
reconciler.updateContainerSync(tree, this.container, null, noop);
294298
// @ts-expect-error the types for `react-reconciler` are not up to date with the library.
295299
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
296300
reconciler.flushSyncWork();
297-
if (this.options.onRender) {
298-
const end = performance.now();
299-
this.options.onRender({renderTime: end - start});
300-
}
301301
}
302302

303303
writeToStdout(data: string): void {

src/render.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Stream} from 'node:stream';
22
import process from 'node:process';
33
import type {ReactNode} from 'react';
4-
import Ink, {type Options as InkOptions, type RenderResult} from './ink.js';
4+
import Ink, {type Options as InkOptions, type RenderMetrics} from './ink.js';
55
import instances from './instances.js';
66

77
export type RenderOptions = {
@@ -47,9 +47,9 @@ export type RenderOptions = {
4747
patchConsole?: boolean;
4848

4949
/**
50-
Runs the given callback after each render and re-render with a RenderResult object.
50+
Runs the given callback after each render and re-render.
5151
*/
52-
onRender?: (renderTime: RenderResult) => void;
52+
onRender?: (metrics: RenderMetrics) => void;
5353

5454
/**
5555
Enable screen reader support. See https://github.com/vadimdemedes/ink/blob/master/readme.md#screen-reader-support

test/render.tsx

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {createRequire} from 'node:module';
66
import FakeTimers from '@sinonjs/fake-timers';
77
import {stub} from 'sinon';
88
import test from 'ava';
9-
import React, {useEffect, useState} from 'react';
9+
import React, {type ReactNode, useEffect, useState} from 'react';
1010
import ansiEscapes from 'ansi-escapes';
1111
import stripAnsi from 'strip-ansi';
1212
import boxen from 'boxen';
1313
import delay from 'delay';
1414
import {render, Box, Text, useInput} from '../src/index.js';
15-
import {type RenderResult} from '../src/ink.js';
15+
import {type RenderMetrics} from '../src/ink.js';
1616
import createStdout from './helpers/create-stdout.js';
1717

1818
const require = createRequire(import.meta.url);
@@ -300,28 +300,18 @@ test.serial('throttle renders to maxFps', t => {
300300
});
301301

302302
test.serial('outputs renderTime when onRender is passed', async t => {
303-
const clock = FakeTimers.install(); // Controls timers + Date.now()
304-
let lastRenderTime = -1;
305-
let tickTime = 100;
303+
const renderTimes: number[] = [];
306304
const funcObj = {
307-
onRender(renderResult: RenderResult) {
308-
const {renderTime} = renderResult;
309-
lastRenderTime = renderTime;
305+
onRender(metrics: RenderMetrics) {
306+
const {renderTime} = metrics;
307+
renderTimes.push(renderTime);
310308
},
311309
};
312310

313311
const onRenderStub = stub(funcObj, 'onRender').callThrough();
314312

315-
function Nested({text}) {
316-
clock.tick(tickTime);
317-
return <Text>{text}</Text>;
318-
}
319-
320-
function Test() {
313+
function Test({children}: {readonly children?: ReactNode}) {
321314
const [text, setText] = useState('Test');
322-
useEffect(() => {
323-
clock.tick(tickTime);
324-
});
325315

326316
useInput(input => {
327317
setText(input);
@@ -330,7 +320,7 @@ test.serial('outputs renderTime when onRender is passed', async t => {
330320
return (
331321
<Box borderStyle="round">
332322
<Text>{text}</Text>
333-
<Nested text={text} />
323+
{children}
334324
</Box>
335325
);
336326
}
@@ -341,24 +331,32 @@ test.serial('outputs renderTime when onRender is passed', async t => {
341331
stdin,
342332
});
343333

344-
t.is(onRenderStub.callCount, 2);
334+
// Initial render
335+
t.is(onRenderStub.callCount, 1);
336+
t.true(renderTimes[0] >= 0);
345337

338+
// Manual rerender
346339
onRenderStub.resetHistory();
347-
tickTime = 200;
348-
rerender(<Test />);
349-
350-
t.is(onRenderStub.callCount, 2);
340+
rerender(
341+
<Test>
342+
<Text>Updated</Text>
343+
</Test>,
344+
);
345+
await delay(100);
346+
t.is(onRenderStub.callCount, 1);
347+
t.true(renderTimes[1] >= 0);
351348

349+
// Internal state update via useInput
352350
onRenderStub.resetHistory();
353351
emitReadable(stdin, 'a');
354352
await delay(100);
355-
356-
t.is(lastRenderTime, 0);
357353
t.is(onRenderStub.callCount, 1);
354+
t.true(renderTimes[2] >= 0);
358355

359-
unmount();
356+
// Verify all renders were tracked
357+
t.is(renderTimes.length, 3);
360358

361-
clock.uninstall();
359+
unmount();
362360
});
363361

364362
test.serial('no throttled renders after unmount', t => {

0 commit comments

Comments
 (0)