From 62037e5689df4f8a04ad5451566ceb830fecc00a Mon Sep 17 00:00:00 2001 From: Patrick Paul Date: Mon, 9 Mar 2026 14:02:00 -0700 Subject: [PATCH] fix(hotkeys): prevent infinite loop in HotkeyHelpModal componentDidUpdate When HotkeyHelpModal opens with no typed hotkeys registered, componentDidUpdate repeatedly calls setState({ currentType: null }) because the guard condition `!prevType` is always true when prevType is already null. This triggers React's maximum update depth error. Skip the setState call entirely when there are no types available. The render method already returns null when currentType is falsy, so the modal correctly won't display. --- src/components/hotkeys/HotkeyHelpModal.js | 4 +-- .../hotkeys/__tests__/HotkeyHelpModal.test.js | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/components/hotkeys/HotkeyHelpModal.js b/src/components/hotkeys/HotkeyHelpModal.js index feed763372..a3ec4f9d04 100644 --- a/src/components/hotkeys/HotkeyHelpModal.js +++ b/src/components/hotkeys/HotkeyHelpModal.js @@ -71,9 +71,9 @@ class HotkeyHelpModal extends Component { this.types = hotkeyLayer.getActiveTypes(); } - if (!prevType) { + if (!prevType && this.types.length) { this.setState({ - currentType: this.types.length ? this.types[0] : null, + currentType: this.types[0], }); } } diff --git a/src/components/hotkeys/__tests__/HotkeyHelpModal.test.js b/src/components/hotkeys/__tests__/HotkeyHelpModal.test.js index 5e842618ed..d060aeaac7 100644 --- a/src/components/hotkeys/__tests__/HotkeyHelpModal.test.js +++ b/src/components/hotkeys/__tests__/HotkeyHelpModal.test.js @@ -83,6 +83,32 @@ describe('components/hotkeys/components/HotkeyHelpModal', () => { expect(instance.state.currentType).toBe('other'); }); + test('should not call setState when no types are available', () => { + const emptyContext = { + getActiveHotkeys: sandbox.stub().returns({}), + getActiveTypes: sandbox.stub().returns([]), + }; + + const wrapper = mount( + } + />, + ); + + const instance = wrapper.find('HotkeyHelpModal').instance(); + const setStateSpy = sandbox.spy(instance, 'setState'); + + act(() => { + wrapper.find('HotkeyTestWrapper').setState({ isOpen: true }); + }); + + // setState should not be called when types are empty, + // preventing an infinite componentDidUpdate loop + expect(setStateSpy.callCount).toBe(0); + }); + test('should refresh hotkey and hotkey types from hotkeyService when modal is opened', () => { const wrapper = mount(