Skip to content

Commit deb9d96

Browse files
authored
Fix undefined errors for focusing unmounted elements (#404)
1 parent 90ae40d commit deb9d96

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

src/components/App.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ export default class App extends PureComponent<Props, State> {
215215

216216
focusNext = (): void => {
217217
this.setState(previousState => {
218-
const firstFocusableId = previousState.focusables[0].id;
218+
const firstFocusableId = previousState.focusables[0]?.id;
219219
const nextFocusableId = this.findNextFocusable(previousState);
220220

221221
return {
@@ -227,8 +227,7 @@ export default class App extends PureComponent<Props, State> {
227227
focusPrevious = (): void => {
228228
this.setState(previousState => {
229229
const lastFocusableId =
230-
previousState.focusables[previousState.focusables.length - 1].id;
231-
230+
previousState.focusables[previousState.focusables.length - 1]?.id;
232231
const previousFocusableId = this.findPreviousFocusable(previousState);
233232

234233
return {
@@ -314,7 +313,7 @@ export default class App extends PureComponent<Props, State> {
314313
index < state.focusables.length;
315314
index++
316315
) {
317-
if (state.focusables[index].isActive) {
316+
if (state.focusables[index]?.isActive) {
318317
return state.focusables[index].id;
319318
}
320319
}
@@ -328,7 +327,7 @@ export default class App extends PureComponent<Props, State> {
328327
});
329328

330329
for (let index = activeIndex - 1; index >= 0; index--) {
331-
if (state.focusables[index].isActive) {
330+
if (state.focusables[index]?.isActive) {
332331
return state.focusables[index].id;
333332
}
334333
}

test/focus.tsx

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const createStdin = () => {
1212
stdin.setRawMode = spy();
1313
stdin.setEncoding = () => {};
1414
stdin.resume = () => {};
15+
stdin.pause = () => {};
1516

1617
return stdin;
1718
};
@@ -23,6 +24,7 @@ interface TestProps {
2324
disabled?: boolean;
2425
focusNext?: boolean;
2526
focusPrevious?: boolean;
27+
unmountChildren?: boolean;
2628
}
2729

2830
const Test: FC<TestProps> = ({
@@ -31,7 +33,8 @@ const Test: FC<TestProps> = ({
3133
autoFocus = false,
3234
disabled = false,
3335
focusNext = false,
34-
focusPrevious = false
36+
focusPrevious = false,
37+
unmountChildren = false
3538
}) => {
3639
const focusManager = useFocusManager();
3740

@@ -55,6 +58,10 @@ const Test: FC<TestProps> = ({
5558
}
5659
}, [focusPrevious]);
5760

61+
if (unmountChildren) {
62+
return null;
63+
}
64+
5865
return (
5966
<Box flexDirection="column">
6067
{showFirst && <Item label="First" autoFocus={autoFocus} />}
@@ -385,3 +392,35 @@ test('manually focus previous component', async t => {
385392
['First', 'Second', 'Third ✔'].join('\n')
386393
);
387394
});
395+
396+
test('doesnt crash when focusing next on unmounted children', async t => {
397+
const stdout = createStdout();
398+
const stdin = createStdin();
399+
const {rerender} = render(<Test autoFocus />, {
400+
stdout,
401+
stdin,
402+
debug: true
403+
});
404+
405+
await delay(100);
406+
rerender(<Test focusNext unmountChildren />);
407+
await delay(100);
408+
409+
t.is(stdout.write.lastCall.args[0], '');
410+
});
411+
412+
test('doesnt crash when focusing previous on unmounted children', async t => {
413+
const stdout = createStdout();
414+
const stdin = createStdin();
415+
const {rerender} = render(<Test autoFocus />, {
416+
stdout,
417+
stdin,
418+
debug: true
419+
});
420+
421+
await delay(100);
422+
rerender(<Test focusPrevious unmountChildren />);
423+
await delay(100);
424+
425+
t.is(stdout.write.lastCall.args[0], '');
426+
});

0 commit comments

Comments
 (0)