diff --git a/packages/core/src/context/hotkeys/hotkeysProvider.tsx b/packages/core/src/context/hotkeys/hotkeysProvider.tsx
index 330d9e2f66..65110eed41 100644
--- a/packages/core/src/context/hotkeys/hotkeysProvider.tsx
+++ b/packages/core/src/context/hotkeys/hotkeysProvider.tsx
@@ -109,7 +109,8 @@ export interface HotkeysProviderProps {
  */
 export const HotkeysProvider = ({ children, dialogProps, renderDialog, value }: HotkeysProviderProps) => {
     const hasExistingContext = value != null;
-    const [state, dispatch] = value ?? React.useReducer(hotkeysReducer, { ...initialHotkeysState, hasProvider: true });
+    const fallbackReducer = React.useReducer(hotkeysReducer, { ...initialHotkeysState, hasProvider: true });
+    const [state, dispatch] = value ?? fallbackReducer;
     const handleDialogClose = React.useCallback(() => dispatch({ type: "CLOSE_DIALOG" }), []);
 
     const dialog = renderDialog?.(state, { handleDialogClose }) ?? (
diff --git a/packages/core/src/hooks/hotkeys/useHotkeys.ts b/packages/core/src/hooks/hotkeys/useHotkeys.ts
index bda89a469d..b386c56efa 100644
--- a/packages/core/src/hooks/hotkeys/useHotkeys.ts
+++ b/packages/core/src/hooks/hotkeys/useHotkeys.ts
@@ -77,9 +77,11 @@ export function useHotkeys(keys: readonly HotkeyConfig[], options: UseHotkeysOpt
     // register keys with global context
     const [state, dispatch] = React.useContext(HotkeysContext);
 
-    if (!state.hasProvider) {
-        React.useEffect(() => console.warn(HOTKEYS_PROVIDER_NOT_FOUND), []);
-    }
+    React.useEffect(() => {
+        if (!state.hasProvider) {
+            console.warn(HOTKEYS_PROVIDER_NOT_FOUND);
+        }
+    }, []);
 
     // we can still bind the hotkeys if there is no HotkeysProvider, they just won't show up in the dialog
     React.useEffect(() => {
diff --git a/packages/docs-app/src/examples/core-examples/useHotkeysExample.tsx b/packages/docs-app/src/examples/core-examples/useHotkeysExample.tsx
index 1acf7d199c..6887f994ce 100644
--- a/packages/docs-app/src/examples/core-examples/useHotkeysExample.tsx
+++ b/packages/docs-app/src/examples/core-examples/useHotkeysExample.tsx
@@ -32,13 +32,13 @@ export const UseHotkeysExample: React.FC<ExampleProps> = props => {
         }
     }, [pianoRef]);
 
-    // create a dictionary of key states and updater functions
-    const keys = Array.apply(null, Array(24))
-        .map(() => React.useState(() => false), [])
-        .map(([pressed, setPressed]) => ({
-            pressed,
-            setPressed,
-        }));
+    const [keyPressed, setKeyPressed] = React.useState<readonly boolean[]>(new Array(25).fill(false));
+
+    const setKeyState = React.useCallback((targetIndex: number, newValue: boolean) => {
+        setKeyPressed(previouslySelected =>
+            previouslySelected.map((value, index) => (index === targetIndex ? newValue : value)),
+        );
+    }, []);
 
     const hotkeys = React.useMemo(
         () => [
@@ -52,169 +52,169 @@ export const UseHotkeysExample: React.FC<ExampleProps> = props => {
                 combo: "Q",
                 group: "useHotkeys Example",
                 label: "Play a C5",
-                onKeyDown: () => keys[0].setPressed(true),
-                onKeyUp: () => keys[0].setPressed(false),
+                onKeyDown: () => setKeyState(0, true),
+                onKeyUp: () => setKeyState(0, false),
             },
             {
                 combo: "2",
                 group: "useHotkeys Example",
                 label: "Play a C#5",
-                onKeyDown: () => keys[1].setPressed(true),
-                onKeyUp: () => keys[1].setPressed(false),
+                onKeyDown: () => setKeyState(1, true),
+                onKeyUp: () => setKeyState(1, false),
             },
             {
                 combo: "W",
                 group: "useHotkeys Example",
                 label: "Play a D5",
-                onKeyDown: () => keys[2].setPressed(true),
-                onKeyUp: () => keys[2].setPressed(false),
+                onKeyDown: () => setKeyState(2, true),
+                onKeyUp: () => setKeyState(2, false),
             },
             {
                 combo: "3",
                 group: "useHotkeys Example",
                 label: "Play a D#5",
-                onKeyDown: () => keys[3].setPressed(true),
-                onKeyUp: () => keys[3].setPressed(false),
+                onKeyDown: () => setKeyState(3, true),
+                onKeyUp: () => setKeyState(3, false),
             },
             {
                 combo: "E",
                 group: "useHotkeys Example",
                 label: "Play a E5",
-                onKeyDown: () => keys[4].setPressed(true),
-                onKeyUp: () => keys[4].setPressed(false),
+                onKeyDown: () => setKeyState(4, true),
+                onKeyUp: () => setKeyState(4, false),
             },
             {
                 combo: "R",
                 group: "useHotkeys Example",
                 label: "Play a F5",
-                onKeyDown: () => keys[5].setPressed(true),
-                onKeyUp: () => keys[5].setPressed(false),
+                onKeyDown: () => setKeyState(5, true),
+                onKeyUp: () => setKeyState(5, false),
             },
             {
                 combo: "5",
                 group: "useHotkeys Example",
                 label: "Play a F#5",
-                onKeyDown: () => keys[6].setPressed(true),
-                onKeyUp: () => keys[6].setPressed(false),
+                onKeyDown: () => setKeyState(6, true),
+                onKeyUp: () => setKeyState(6, false),
             },
             {
                 combo: "T",
                 group: "useHotkeys Example",
                 label: "Play a G5",
-                onKeyDown: () => keys[7].setPressed(true),
-                onKeyUp: () => keys[7].setPressed(false),
+                onKeyDown: () => setKeyState(7, true),
+                onKeyUp: () => setKeyState(7, false),
             },
             {
                 combo: "6",
                 group: "useHotkeys Example",
                 label: "Play a G#5",
-                onKeyDown: () => keys[8].setPressed(true),
-                onKeyUp: () => keys[8].setPressed(false),
+                onKeyDown: () => setKeyState(8, true),
+                onKeyUp: () => setKeyState(8, false),
             },
             {
                 combo: "Y",
                 group: "useHotkeys Example",
                 label: "Play a A5",
-                onKeyDown: () => keys[9].setPressed(true),
-                onKeyUp: () => keys[9].setPressed(false),
+                onKeyDown: () => setKeyState(9, true),
+                onKeyUp: () => setKeyState(9, false),
             },
             {
                 combo: "7",
                 group: "useHotkeys Example",
                 label: "Play a A#5",
-                onKeyDown: () => keys[10].setPressed(true),
-                onKeyUp: () => keys[10].setPressed(false),
+                onKeyDown: () => setKeyState(10, true),
+                onKeyUp: () => setKeyState(10, false),
             },
             {
                 combo: "U",
                 group: "useHotkeys Example",
                 label: "Play a B5",
-                onKeyDown: () => keys[11].setPressed(true),
-                onKeyUp: () => keys[11].setPressed(false),
+                onKeyDown: () => setKeyState(11, true),
+                onKeyUp: () => setKeyState(11, false),
             },
             {
                 combo: "Z",
                 group: "useHotkeys Example",
                 label: "Play a C4",
-                onKeyDown: () => keys[12].setPressed(true),
-                onKeyUp: () => keys[12].setPressed(false),
+                onKeyDown: () => setKeyState(12, true),
+                onKeyUp: () => setKeyState(12, false),
             },
             {
                 combo: "S",
                 group: "useHotkeys Example",
                 label: "Play a C#4",
-                onKeyDown: () => keys[13].setPressed(true),
-                onKeyUp: () => keys[13].setPressed(false),
+                onKeyDown: () => setKeyState(13, true),
+                onKeyUp: () => setKeyState(13, false),
             },
             {
                 combo: "X",
                 group: "useHotkeys Example",
                 label: "Play a D4",
-                onKeyDown: () => keys[14].setPressed(true),
-                onKeyUp: () => keys[14].setPressed(false),
+                onKeyDown: () => setKeyState(14, true),
+                onKeyUp: () => setKeyState(14, false),
             },
             {
                 combo: "D",
                 group: "useHotkeys Example",
                 label: "Play a D#4",
-                onKeyDown: () => keys[15].setPressed(true),
-                onKeyUp: () => keys[15].setPressed(false),
+                onKeyDown: () => setKeyState(15, true),
+                onKeyUp: () => setKeyState(15, false),
             },
             {
                 combo: "C",
                 group: "useHotkeys Example",
                 label: "Play a E4",
-                onKeyDown: () => keys[16].setPressed(true),
-                onKeyUp: () => keys[16].setPressed(false),
+                onKeyDown: () => setKeyState(16, true),
+                onKeyUp: () => setKeyState(16, false),
             },
             {
                 combo: "V",
                 group: "useHotkeys Example",
                 label: "Play a F4",
-                onKeyDown: () => keys[17].setPressed(true),
-                onKeyUp: () => keys[17].setPressed(false),
+                onKeyDown: () => setKeyState(17, true),
+                onKeyUp: () => setKeyState(17, false),
             },
             {
                 combo: "G",
                 group: "useHotkeys Example",
                 label: "Play a F#4",
-                onKeyDown: () => keys[18].setPressed(true),
-                onKeyUp: () => keys[18].setPressed(false),
+                onKeyDown: () => setKeyState(18, true),
+                onKeyUp: () => setKeyState(18, false),
             },
             {
                 combo: "B",
                 group: "useHotkeys Example",
                 label: "Play a G4",
-                onKeyDown: () => keys[19].setPressed(true),
-                onKeyUp: () => keys[19].setPressed(false),
+                onKeyDown: () => setKeyState(19, true),
+                onKeyUp: () => setKeyState(19, false),
             },
             {
                 combo: "H",
                 group: "useHotkeys Example",
                 label: "Play a G#4",
-                onKeyDown: () => keys[20].setPressed(true),
-                onKeyUp: () => keys[20].setPressed(false),
+                onKeyDown: () => setKeyState(20, true),
+                onKeyUp: () => setKeyState(20, false),
             },
             {
                 combo: "N",
                 group: "useHotkeys Example",
                 label: "Play a A4",
-                onKeyDown: () => keys[21].setPressed(true),
-                onKeyUp: () => keys[21].setPressed(false),
+                onKeyDown: () => setKeyState(21, true),
+                onKeyUp: () => setKeyState(21, false),
             },
             {
                 combo: "J",
                 group: "useHotkeys Example",
                 label: "Play a A#4",
-                onKeyDown: () => keys[22].setPressed(true),
-                onKeyUp: () => keys[22].setPressed(false),
+                onKeyDown: () => setKeyState(22, true),
+                onKeyUp: () => setKeyState(22, false),
             },
             {
                 combo: "M",
                 group: "useHotkeys Example",
                 label: "Play a B4",
-                onKeyDown: () => keys[23].setPressed(true),
-                onKeyUp: () => keys[23].setPressed(false),
+                onKeyDown: () => setKeyState(23, true),
+                onKeyUp: () => setKeyState(23, false),
             },
         ],
         [],
@@ -232,32 +232,32 @@ export const UseHotkeysExample: React.FC<ExampleProps> = props => {
                 onKeyUp={handleKeyUp}
             >
                 <div>
-                    <PianoKey note="C5" hotkey="Q" pressed={keys[0].pressed} context={audioContext} />
-                    <PianoKey note="C#5" hotkey="2" pressed={keys[1].pressed} context={audioContext} />
-                    <PianoKey note="D5" hotkey="W" pressed={keys[2].pressed} context={audioContext} />
-                    <PianoKey note="D#5" hotkey="3" pressed={keys[3].pressed} context={audioContext} />
-                    <PianoKey note="E5" hotkey="E" pressed={keys[4].pressed} context={audioContext} />
-                    <PianoKey note="F5" hotkey="R" pressed={keys[5].pressed} context={audioContext} />
-                    <PianoKey note="F#5" hotkey="5" pressed={keys[6].pressed} context={audioContext} />
-                    <PianoKey note="G5" hotkey="T" pressed={keys[7].pressed} context={audioContext} />
-                    <PianoKey note="G#5" hotkey="6" pressed={keys[8].pressed} context={audioContext} />
-                    <PianoKey note="A5" hotkey="Y" pressed={keys[9].pressed} context={audioContext} />
-                    <PianoKey note="A#5" hotkey="7" pressed={keys[10].pressed} context={audioContext} />
-                    <PianoKey note="B5" hotkey="U" pressed={keys[11].pressed} context={audioContext} />
+                    <PianoKey note="C5" hotkey="Q" pressed={keyPressed[0]} context={audioContext} />
+                    <PianoKey note="C#5" hotkey="2" pressed={keyPressed[1]} context={audioContext} />
+                    <PianoKey note="D5" hotkey="W" pressed={keyPressed[2]} context={audioContext} />
+                    <PianoKey note="D#5" hotkey="3" pressed={keyPressed[3]} context={audioContext} />
+                    <PianoKey note="E5" hotkey="E" pressed={keyPressed[4]} context={audioContext} />
+                    <PianoKey note="F5" hotkey="R" pressed={keyPressed[5]} context={audioContext} />
+                    <PianoKey note="F#5" hotkey="5" pressed={keyPressed[6]} context={audioContext} />
+                    <PianoKey note="G5" hotkey="T" pressed={keyPressed[7]} context={audioContext} />
+                    <PianoKey note="G#5" hotkey="6" pressed={keyPressed[8]} context={audioContext} />
+                    <PianoKey note="A5" hotkey="Y" pressed={keyPressed[9]} context={audioContext} />
+                    <PianoKey note="A#5" hotkey="7" pressed={keyPressed[10]} context={audioContext} />
+                    <PianoKey note="B5" hotkey="U" pressed={keyPressed[11]} context={audioContext} />
                 </div>
                 <div>
-                    <PianoKey note="C4" hotkey="Z" pressed={keys[12].pressed} context={audioContext} />
-                    <PianoKey note="C#4" hotkey="S" pressed={keys[13].pressed} context={audioContext} />
-                    <PianoKey note="D4" hotkey="X" pressed={keys[14].pressed} context={audioContext} />
-                    <PianoKey note="D#4" hotkey="D" pressed={keys[15].pressed} context={audioContext} />
-                    <PianoKey note="E4" hotkey="C" pressed={keys[16].pressed} context={audioContext} />
-                    <PianoKey note="F4" hotkey="V" pressed={keys[17].pressed} context={audioContext} />
-                    <PianoKey note="F#4" hotkey="G" pressed={keys[18].pressed} context={audioContext} />
-                    <PianoKey note="G4" hotkey="B" pressed={keys[19].pressed} context={audioContext} />
-                    <PianoKey note="G#4" hotkey="H" pressed={keys[20].pressed} context={audioContext} />
-                    <PianoKey note="A4" hotkey="N" pressed={keys[21].pressed} context={audioContext} />
-                    <PianoKey note="A#4" hotkey="J" pressed={keys[22].pressed} context={audioContext} />
-                    <PianoKey note="B4" hotkey="M" pressed={keys[23].pressed} context={audioContext} />
+                    <PianoKey note="C4" hotkey="Z" pressed={keyPressed[12]} context={audioContext} />
+                    <PianoKey note="C#4" hotkey="S" pressed={keyPressed[13]} context={audioContext} />
+                    <PianoKey note="D4" hotkey="X" pressed={keyPressed[14]} context={audioContext} />
+                    <PianoKey note="D#4" hotkey="D" pressed={keyPressed[15]} context={audioContext} />
+                    <PianoKey note="E4" hotkey="C" pressed={keyPressed[16]} context={audioContext} />
+                    <PianoKey note="F4" hotkey="V" pressed={keyPressed[17]} context={audioContext} />
+                    <PianoKey note="F#4" hotkey="G" pressed={keyPressed[18]} context={audioContext} />
+                    <PianoKey note="G4" hotkey="B" pressed={keyPressed[19]} context={audioContext} />
+                    <PianoKey note="G#4" hotkey="H" pressed={keyPressed[20]} context={audioContext} />
+                    <PianoKey note="A4" hotkey="N" pressed={keyPressed[21]} context={audioContext} />
+                    <PianoKey note="A#4" hotkey="J" pressed={keyPressed[22]} context={audioContext} />
+                    <PianoKey note="B4" hotkey="M" pressed={keyPressed[23]} context={audioContext} />
                 </div>
             </div>
         </Example>
diff --git a/packages/docs-theme/src/tags/css.tsx b/packages/docs-theme/src/tags/css.tsx
index 9f23834abe..2edbf41237 100644
--- a/packages/docs-theme/src/tags/css.tsx
+++ b/packages/docs-theme/src/tags/css.tsx
@@ -31,6 +31,16 @@ export const CssExample: React.FC<ITag> = ({ value }) => {
     const { getDocsData } = React.useContext(DocumentationContext);
     const [activeModifiers, setActiveModifiers] = React.useState<Set<string>>(new Set());
 
+    const getModifiers = React.useCallback(
+        (prefix: "." | ":") => {
+            return Array.from(activeModifiers.keys())
+                .filter(mod => mod.charAt(0) === prefix)
+                .map(mod => mod.slice(1))
+                .join(" ");
+        },
+        [activeModifiers],
+    );
+
     const getModifierToggleHandler = (modifier: string) => {
         return () => {
             const newModifiers = new Set(activeModifiers);
@@ -60,15 +70,6 @@ export const CssExample: React.FC<ITag> = ({ value }) => {
         </Checkbox>
     ));
 
-    const getModifiers = React.useCallback(
-        (prefix: "." | ":") => {
-            return Array.from(activeModifiers.keys())
-                .filter(mod => mod.charAt(0) === prefix)
-                .map(mod => mod.slice(1))
-                .join(" ");
-        },
-        [activeModifiers],
-    );
     const classModifiers = getModifiers(".");
     const attrModifiers = getModifiers(":");
     const exampleHtml = markup
diff --git a/packages/eslint-config/eslint-plugin-rules.json b/packages/eslint-config/eslint-plugin-rules.json
index e9ef2144e1..cae6ce8b4f 100644
--- a/packages/eslint-config/eslint-plugin-rules.json
+++ b/packages/eslint-config/eslint-plugin-rules.json
@@ -57,5 +57,7 @@
     "react/no-direct-mutation-state": "error",
     "react/no-find-dom-node": "error",
     "react/no-string-refs": "error",
-    "react/self-closing-comp": "error"
+    "react/self-closing-comp": "error",
+    "react-hooks/rules-of-hooks": "error",
+    "react-hooks/exhaustive-deps": "warn"
 }
diff --git a/packages/eslint-config/index.js b/packages/eslint-config/index.js
index bee301b999..d4dcc643e0 100644
--- a/packages/eslint-config/index.js
+++ b/packages/eslint-config/index.js
@@ -25,7 +25,7 @@ const tsEslintRules = require("./typescript-eslint-rules.json");
  * For TS files, configure typescript-eslint, including type-aware lint rules which use the TS program.
  */
 module.exports = {
-    plugins: ["@blueprintjs", "header", "import", "jsdoc", "react"],
+    plugins: ["@blueprintjs", "header", "import", "jsdoc", "react", "react-hooks"],
     extends: ["plugin:@blueprintjs/recommended", "plugin:import/typescript"],
     parserOptions: { ecmaVersion: 2022 },
     settings: {
diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json
index 1bd7b7c492..487de5f8f0 100644
--- a/packages/eslint-config/package.json
+++ b/packages/eslint-config/package.json
@@ -13,7 +13,8 @@
         "eslint-plugin-header": "^3.1.1",
         "eslint-plugin-import": "~2.26.0",
         "eslint-plugin-jsdoc": "^46.2.4",
-        "eslint-plugin-react": "^7.32.2"
+        "eslint-plugin-react": "^7.32.2",
+        "eslint-plugin-react-hooks": "^4.6.0"
     },
     "repository": {
         "type": "git",
diff --git a/yarn.lock b/yarn.lock
index ff357c3373..c98fe3f023 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4697,6 +4697,11 @@ eslint-plugin-prettier@^4.2.1:
   dependencies:
     prettier-linter-helpers "^1.0.0"
 
+eslint-plugin-react-hooks@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
+  integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
+
 eslint-plugin-react@^7.32.2:
   version "7.32.2"
   resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10"