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

fix(useAccessibility): make callbacks referentially stable #12185

Merged
merged 11 commits into from
Mar 9, 2020

Conversation

layershifter
Copy link
Member

@layershifter layershifter commented Mar 4, 2020

Pull request checklist

  • Addresses an existing issue: Fixes #0000
  • Include a change request file using $ yarn change

Description of changes

This PR makes returned onKeyDown by getA11yProps from useAccessibility() hook stable between rerenders, so it will stop to break memoized component (PureComponent, React.memo()) which can be used via as prop or as trigger in Popup & Tooltip.

Microsoft Reviewers: Open in CodeFlow

@size-auditor
Copy link

size-auditor bot commented Mar 4, 2020

Asset size changes

Size Auditor did not detect a change in bundle size for any component!

Baseline commit: 49b255f5808c36413b36866c0ead9dcfa41d3252 (build)

@msft-github-bot
Copy link
Contributor

msft-github-bot commented Mar 4, 2020

Perf Analysis

No significant results to display.

All results

Scenario Master Ticks PR Ticks Status
BaseButton 618 634
BaseButton (experiments) 807 792
DefaultButton 824 835
DefaultButton (experiments) 1553 1547
DetailsRow 2802 2849
DetailsRow (fast icons) 2944 2861
DetailsRow without styles 2759 2692
DocumentCardTitle with truncation 1507 1497
MenuButton 1219 1210
MenuButton (experiments) 3262 3125
PrimaryButton 1082 1064
PrimaryButton (experiments) 1734 1797
SplitButton 2632 2630
SplitButton (experiments) 6047 6306
Stack 392 373
Stack with Intrinsic children 954 938
Stack with Text children 3488 3493
Text 305 318
Toggle 736 680
Toggle (experiments) 1906 1946
button 62 46

Perf Analysis (Fluent)

Perf comparison
Status Scenario Fluent TPI Fabric TPI Ratio Iterations Ticks
🔧 Avatar.Fluent 0.47 0.44 1.07:1 2000 935
🦄 Button.Fluent 0.13 0.19 0.68:1 1000 125
🔧 Checkbox.Fluent 0.84 0.33 2.55:1 1000 844
🔧 Dialog.Fluent 0.31 0.19 1.63:1 5000 1554
🔧 Dropdown.Fluent 3.38 0.43 7.86:1 1000 3383
🔧 Icon.Fluent 0.15 0.04 3.75:1 5000 774
🦄 Image.Fluent 0.06 0.1 0.6:1 5000 324
🔧 Slider.Fluent 1.46 0.37 3.95:1 1000 1462
🔧 Text.Fluent 0.07 0.02 3.5:1 5000 340
🦄 Tooltip.Fluent 0.11 16.53 0.01:1 5000 535

🔧 Needs work     🎯 On target     🦄 Amazing

Perf tests with no regressions
Scenario Current PR Ticks Baseline Ticks Ratio
Image.Fluent 324 262 1.24:1
LabelMinimalPerf.default 351 286 1.23:1
HeaderMinimalPerf.default 473 401 1.18:1
ListMinimalPerf.default 383 334 1.15:1
RefMinimalPerf.default 185 166 1.11:1
IconMinimalPerf.default 345 315 1.1:1
ImageMinimalPerf.default 259 235 1.1:1
ListCommonPerf.default 902 830 1.09:1
AnimationMinimalPerf.default 586 545 1.08:1
ButtonMinimalPerf.default 142 132 1.08:1
FlexMinimalPerf.default 219 203 1.08:1
ListNestedPerf.default 839 780 1.08:1
DropdownManyItemsPerf.default 335 314 1.07:1
BoxMinimalPerf.default 316 299 1.06:1
TextMinimalPerf.default 296 279 1.06:1
Text.Fluent 340 322 1.06:1
ChatDuplicateMessagesPerf.default 407 388 1.05:1
ChatMinimalPerf.default 508 485 1.05:1
ChatWithPopoverPerf.default 576 549 1.05:1
ButtonSlotsPerf.default 700 670 1.04:1
DialogMinimalPerf.default 1619 1558 1.04:1
GridMinimalPerf.default 737 711 1.04:1
HierarchicalTreeMinimalPerf.default 873 838 1.04:1
TableMinimalPerf.default 587 562 1.04:1
CheckboxMinimalPerf.default 3921 3789 1.03:1
EmbedMinimalPerf.default 5782 5598 1.03:1
RadioGroupMinimalPerf.default 483 468 1.03:1
Avatar.Fluent 935 907 1.03:1
Icon.Fluent 774 751 1.03:1
DropdownMinimalPerf.default 3345 3276 1.02:1
ItemLayoutMinimalPerf.default 1800 1772 1.02:1
MenuButtonMinimalPerf.default 1738 1699 1.02:1
ProviderMergeThemesPerf.default 1244 1220 1.02:1
SplitButtonMinimalPerf.default 11956 11747 1.02:1
Tooltip.Fluent 535 522 1.02:1
AttachmentSlotsPerf.default 3412 3375 1.01:1
PopupMinimalPerf.default 369 364 1.01:1
CustomToolbarPrototype.default 3493 3471 1.01:1
ToolbarMinimalPerf.default 989 978 1.01:1
HeaderSlotsPerf.default 1448 1451 1:1
LoaderMinimalPerf.default 974 974 1:1
MenuMinimalPerf.default 1849 1844 1:1
PortalMinimalPerf.default 261 260 1:1
ReactionMinimalPerf.default 2297 2297 1:1
SegmentMinimalPerf.default 1000 998 1:1
StatusMinimalPerf.default 280 280 1:1
TextAreaMinimalPerf.default 2953 2966 1:1
TooltipMinimalPerf.default 736 739 1:1
Checkbox.Fluent 844 844 1:1
Dropdown.Fluent 3383 3379 1:1
LayoutMinimalPerf.default 544 552 0.99:1
ListWith60ListItems.default 158 159 0.99:1
SliderMinimalPerf.default 1426 1437 0.99:1
TreeMinimalPerf.default 959 971 0.99:1
AttachmentMinimalPerf.default 880 895 0.98:1
FormMinimalPerf.default 794 809 0.98:1
ProviderMinimalPerf.default 599 609 0.98:1
Slider.Fluent 1462 1485 0.98:1
CarouselMinimalPerf.default 1897 1948 0.97:1
InputMinimalPerf.default 926 962 0.96:1
Dialog.Fluent 1554 1624 0.96:1
AlertMinimalPerf.default 542 571 0.95:1
DividerMinimalPerf.default 876 924 0.95:1
TreeWith60ListItems.default 196 211 0.93:1
VideoMinimalPerf.default 772 830 0.93:1
Button.Fluent 125 134 0.93:1
AccordionMinimalPerf.default 222 242 0.92:1
AvatarMinimalPerf.default 494 547 0.9:1

const definition = getAccessibility(debugName, behavior, mapPropsToBehavior(), rtl, actionHandlers);
if (!slotHandlers.current[slotName]) {
slotHandlers.current[slotName] = (e, ...args) => {
const accessibilityHandler = definition.keyHandlers[slotName].onKeyDown;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keyHandlers for a slot can be undefined. That was previously checked on line 38 but you don't have the check in the new code.

Also the previous contract was that the onKeyDown listener was added to an element only if it was handling anything. And that could change based on props change. Now the listener is added and never removed.
I think it would be better to be unstable in these edge cases and add/remove the listener when needed.

You can use ButtonBehavior to test both issues.

  • It does not add the listener by default -> first issue.
  • If you rerender with as='div', it is supposed to add the listener, if you remove theas later, it should remove the listener -> second issue.

@@ -11,7 +11,7 @@ export interface ReactAccessibilityBehavior extends AccessibilityDefinition {
}

export type AccessibilityKeyHandlers = {
[slotName: string]: AccessibilityHandlerProps;
[slotName: string]: AccessibilityHandlerProps | undefined;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be undefined, yep

@@ -2,6 +2,7 @@ import * as React from 'react';

// useLayoutEffect() produces a warning with SSR rendering
// https://medium.com/@alexandereardon/uselayouteffect-and-ssr-192986cdcf7a
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' && process.env.NODE_ENV !== 'test' ? React.useLayoutEffect : React.useEffect;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without process.env.NODE_ENV !== 'test' it produces warning for tests because we use JSDom which has window for sure

@layershifter layershifter changed the title fix(useAccessibility): make callbacks referentially stable [WIP] fix(useAccessibility): make callbacks referentially stable Mar 6, 2020
@layershifter layershifter merged commit a6b49f2 into master Mar 9, 2020
@layershifter layershifter deleted the fix/use-acc branch March 9, 2020 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Fluent UI react-northstar (v0) Work related to Fluent UI V0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants