Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable react-hooks ESLint rules & fix associated errors #6277

Merged
merged 2 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/core/src/context/hotkeys/hotkeysProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) ?? (
Expand Down
8 changes: 5 additions & 3 deletions packages/core/src/hooks/hotkeys/useHotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down
158 changes: 79 additions & 79 deletions packages/docs-app/src/examples/core-examples/useHotkeysExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
() => [
Expand All @@ -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),
},
],
[],
Expand All @@ -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>
Expand Down
19 changes: 10 additions & 9 deletions packages/docs-theme/src/tags/css.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion packages/eslint-config/eslint-plugin-rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
2 changes: 1 addition & 1 deletion packages/eslint-config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
3 changes: 2 additions & 1 deletion packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down