Skip to content

Commit

Permalink
Start enabling "rules of hooks" lint rules (microsoft#14097)
Browse files Browse the repository at this point in the history
  • Loading branch information
ecraig12345 committed Jul 21, 2020
1 parent 4b8d8bc commit 63b41e9
Show file tree
Hide file tree
Showing 36 changed files with 248 additions and 121 deletions.
1 change: 1 addition & 0 deletions apps/perf-test/src/app/useTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const useTimer = () => {
setVisible(false);
}, 5000);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isRunning]);

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "patch",
"comment": "Enable react-hooks lint rules",
"packageName": "@fluentui/eslint-plugin",
"email": "elcraig@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-07-18T04:52:18.203Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "none",
"comment": "Disable react-hooks lint rules for now",
"packageName": "@fluentui/react-next",
"email": "elcraig@microsoft.com",
"dependentChangeType": "none",
"date": "2020-07-18T04:53:18.328Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "patch",
"comment": "Fix \"rules of hooks\" lint rule violations",
"packageName": "@fluentui/react-stylesheets",
"email": "elcraig@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-07-18T04:53:28.129Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "none",
"comment": "Disable react-hooks lint rules for now",
"packageName": "@uifabric/date-time",
"email": "elcraig@microsoft.com",
"dependentChangeType": "none",
"date": "2020-07-18T04:52:08.572Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "none",
"comment": "Disable react-hooks lint rules for now.",
"packageName": "@uifabric/experiments",
"email": "humbertomakotomorimoto@gmail.com",
"dependentChangeType": "none",
"date": "2020-07-20T17:53:10.538Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "patch",
"comment": "Fix \"rules of hooks\" lint rule violations",
"packageName": "@uifabric/foundation",
"email": "elcraig@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-07-18T04:52:49.183Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "none",
"comment": "Disable react-hooks lint rules for now",
"packageName": "@uifabric/react-hooks",
"email": "elcraig@microsoft.com",
"dependentChangeType": "none",
"date": "2020-07-18T04:53:13.903Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "patch",
"comment": "Fix \"rules of hooks\" lint rule violations",
"packageName": "@uifabric/tsx-editor",
"email": "elcraig@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-07-18T04:53:43.812Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "none",
"comment": "Disable react-hooks lint rules for now",
"packageName": "@uifabric/utilities",
"email": "elcraig@microsoft.com",
"dependentChangeType": "none",
"date": "2020-07-18T04:53:53.969Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "patch",
"comment": "Fix \"rules of hooks\" lint rule violations",
"packageName": "office-ui-fabric-react",
"email": "elcraig@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-07-18T04:53:02.288Z"
}
7 changes: 6 additions & 1 deletion packages/date-time/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"extends": ["plugin:@fluentui/eslint-plugin/react--legacy"],
"root": true
"root": true,
"rules": {
// Disable until issues are dealt with
"react-hooks/exhaustive-deps": "off",
"react-hooks/rules-of-hooks": "off"
}
}
2 changes: 1 addition & 1 deletion packages/eslint-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"eslint-plugin-jest": "^23.13.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.20.0",
"eslint-plugin-react-hooks": "^4.0.4",
"eslint-plugin-react-hooks": "^4.0.8",
"jju": "^1.4.0"
},
"devDependencies": {
Expand Down
5 changes: 2 additions & 3 deletions packages/eslint-plugin/src/configs/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ const config = {
],
'react/no-string-refs': 'error',
'react/self-closing-comp': 'error',
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',

// airbnb or other config overrides (some temporary)
// TODO: determine which rules we want to enable, and make needed changes (separate PR)
Expand Down Expand Up @@ -193,9 +195,6 @@ const config = {
'react/static-property-placement': 'off',
'spaced-comment': 'off',

// Enable ASAP (not done in this PR to make resulting changes reviewable)
'react-hooks/exhaustive-deps': 'off',
'react-hooks/rules-of-hooks': 'off',
// airbnb options ban for-of which is unnecessary for TS and modern node (https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb-base/rules/style.js#L334)
// but this is a very powerful rule we may want to use in other ways
'no-restricted-syntax': 'off',
Expand Down
5 changes: 4 additions & 1 deletion packages/experiments/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"extends": ["plugin:@fluentui/eslint-plugin/react--legacy"],
"root": true,
"rules": {
"@typescript-eslint/no-explicit-any": "off"
"@typescript-eslint/no-explicit-any": "off",
// Disable until issues are dealt with
"react-hooks/exhaustive-deps": "off",
"react-hooks/rules-of-hooks": "off"
}
}
16 changes: 8 additions & 8 deletions packages/foundation/src/createComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function createComponent<
const { factoryOptions = {} } = options;
const { defaultProp } = factoryOptions;

const result: React.FunctionComponent<TComponentProps> = (
const ResultComponent: React.FunctionComponent<TComponentProps> = (
componentProps: TComponentProps & IStyleableComponentProps<TViewProps, TTokens, TStyleSet>,
) => {
const settings: ICustomizationProps<TViewProps, TTokens, TStyleSet> = _getCustomizations(
Expand All @@ -56,13 +56,13 @@ export function createComponent<
options.fields,
);

const useState = options.state;
const stateReducer = options.state;

if (useState) {
if (stateReducer) {
// Don't assume state will return all props, so spread useState result over component props.
componentProps = {
...componentProps,
...useState(componentProps),
...stateReducer(componentProps),
};
}

Expand All @@ -89,19 +89,19 @@ export function createComponent<
return view(viewProps);
};

result.displayName = options.displayName || view.name;
ResultComponent.displayName = options.displayName || view.name;

// If a shorthand prop is defined, create a factory for the component.
// TODO: This shouldn't be a concern of createComponent.. factoryOptions should just be forwarded.
// Need to weigh creating default factories on component creation vs. memoizing them on use in slots.tsx.
if (defaultProp) {
(result as ISlotCreator<TComponentProps, any>).create = createFactory(result, { defaultProp });
(ResultComponent as ISlotCreator<TComponentProps, any>).create = createFactory(ResultComponent, { defaultProp });
}

assign(result, options.statics);
assign(ResultComponent, options.statics);

// Later versions of TypeSript should allow us to merge objects in a type safe way and avoid this cast.
return result as React.FunctionComponent<TComponentProps> & TStatics;
return ResultComponent as React.FunctionComponent<TComponentProps> & TStatics;
}

/**
Expand Down
32 changes: 23 additions & 9 deletions packages/foundation/src/next/composed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,14 @@ export function composed<
const { factoryOptions = {}, view } = options;
const { defaultProp } = factoryOptions;

const result: IFoundationComponent<TComponentProps, TTokens, TStyleSet, TViewProps, TComponentSlots, TStatics> = (
const ResultComponent: IFoundationComponent<
TComponentProps,
TTokens,
TStyleSet,
TViewProps,
TComponentSlots,
TStatics
> = (
componentProps: TViewProps &
IStyleableComponentProps<TViewProps, TTokens, TStyleSet> & { children?: React.ReactNode },
) => {
Expand All @@ -162,13 +169,13 @@ export function composed<
options.fields,
);

const useState = options.state;
const stateReducer = options.state;

if (useState) {
if (stateReducer) {
// Don't assume state will return all props, so spread useState result over component props.
componentProps = {
...componentProps,
...useState(componentProps),
...stateReducer(componentProps),
};
}

Expand Down Expand Up @@ -263,21 +270,28 @@ export function composed<
return view ? view(viewProps, Slots) : null;
};

result.displayName = options.displayName || (view && view.name);
ResultComponent.displayName = options.displayName || (view && view.name);

// If a shorthand prop is defined, create a factory for the component.
// TODO: This shouldn't be a concern of createComponent.. factoryOptions should just be forwarded.
// Need to weigh creating default factories on component creation vs. memoizing them on use in slots.tsx.
if (defaultProp) {
(result as ISlotCreator<TComponentProps, any>).create = createFactory(result, { defaultProp });
(ResultComponent as ISlotCreator<TComponentProps, any>).create = createFactory(ResultComponent, { defaultProp });
}

result.__options = options;
ResultComponent.__options = options;

assign(result, options.statics);
assign(ResultComponent, options.statics);

// Later versions of TypeSript should allow us to merge objects in a type safe way and avoid this cast.
return result as IFoundationComponent<TComponentProps, TTokens, TStyleSet, TViewProps, TComponentSlots, TStatics> &
return ResultComponent as IFoundationComponent<
TComponentProps,
TTokens,
TStyleSet,
TViewProps,
TComponentSlots,
TStatics
> &
TStatics;
}

Expand Down
7 changes: 6 additions & 1 deletion packages/lists/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"extends": ["plugin:@fluentui/eslint-plugin/react--legacy"],
"root": true
"root": true,
"rules": {
// Disable until issues are dealt with
"react-hooks/exhaustive-deps": "off",
"react-hooks/rules-of-hooks": "off"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const IndividualCommandBarButtonAsExample: React.FunctionComponent<IIndividualCo
onClick: () => console.log('Download'),
},
];
}, [props.onDismissCoachmark, props.isCoachmarkVisible]);
}, [onDismissCoachmark, isCoachmarkVisible]);

return (
<CommandBar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const CommandBarLazyExample: React.FunctionComponent = () => {
},
},
];
}, [menuItems]);
}, [menuItems, loadItems, onMenuDismissed]);

return (
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ import {
IContextualMenuItemProps,
} from 'office-ui-fabric-react/lib/ContextualMenu';
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { useConst } from '@uifabric/react-hooks';

export const ContextualMenuWithCustomMenuItemExample: React.FunctionComponent = () => {
const menuProps: IContextualMenuProps = React.useMemo(
() => ({
items: menuItems,
shouldFocusOnMount: true,
contextualMenuItemAs: (props: IContextualMenuItemProps) => <div>Custom rendered {props.item.text}</div>,
}),
[menuItems],
);
const menuProps: IContextualMenuProps = useConst(() => ({
items: menuItems,
shouldFocusOnMount: true,
contextualMenuItemAs: (props: IContextualMenuItemProps) => <div>Custom rendered {props.item.text}</div>,
}));

return <DefaultButton text="Click for ContextualMenu" menuProps={menuProps} />;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { useConstCallback } from '@uifabric/react-hooks';
import { useConstCallback, useConst } from '@uifabric/react-hooks';
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { ISearchBoxStyles, SearchBox } from 'office-ui-fabric-react/lib/SearchBox';
import { Icon } from 'office-ui-fabric-react/lib/Icon';
Expand Down Expand Up @@ -52,15 +52,12 @@ export const ContextualMenuWithCustomMenuListExample: React.FunctionComponent =
},
);

const menuProps = React.useMemo(
() => ({
onRenderMenuList: renderMenuList,
title: 'Actions',
shouldFocusOnMount: true,
items,
}),
[items],
);
const menuProps = useConst(() => ({
onRenderMenuList: renderMenuList,
title: 'Actions',
shouldFocusOnMount: true,
items,
}));

return <DefaultButton text="Click for ContextualMenu" menuProps={menuProps} />;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const ContextualMenuDirectionalExample: React.FunctionComponent = () => {
directionalHintFixed: false,
items: menuItems,
}),
[isBeakVisible, directionalHint, directionalHintForRTL],
[isBeakVisible, directionalHint, directionalHintForRTL, useDirectionalHintForRTL],
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const DialogBasicExample: React.FunctionComponent = () => {
styles: dialogStyles,
dragOptions: isDraggable ? dragOptions : undefined,
}),
[isDraggable],
[isDraggable, labelId, subTextId],
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,27 @@ export const HoverCardPageProps: IDocPageProps = {
'https://github.com/microsoft/fluentui/tree/master/packages/office-ui-fabric-react/src/components/HoverCard',
examples: [
{
title: 'Example 1: Expanding HoverCard wrapping an element',
title: 'Expanding HoverCard wrapping an element',
code: HoverCardBasicExampleCode,
view: <HoverCardBasicExample />,
},
{
title: 'Example 2: Expanding HoverCard using Target, DirectionalHint and custom HotKey',
title: 'Expanding HoverCard using target, DirectionalHint and custom hotkey',
code: HoverCardTargetExampleCode,
view: <HoverCardTargetExample />,
},
{
title: 'Example 3: Plain HoverCard wrapping an element',
title: 'Plain HoverCard wrapping an element',
code: HoverCardPlainCardExampleCode,
view: <HoverCardPlainCardExample />,
},
{
title: 'Example 4: Plain HoverCard with instant dismiss from within the card button click',
title: 'Plain HoverCard with instant dismiss from within the card button click',
code: HoverCardInstantDismissExampleCode,
view: <HoverCardInstantDismissExample />,
},
{
title: 'Example 5: HoverCard using eventListenerTarget to trigger card open',
title: 'HoverCard using eventListenerTarget to trigger card open',
code: HoverCardEventListenerTargetExampleCode,
view: <HoverCardEventListenerTargetExample />,
},
Expand Down
Loading

0 comments on commit 63b41e9

Please sign in to comment.