Semantic-ui-react: Dropdown: componentWillReceiveProps performs deep equal on React component

Created on 12 Jul 2018  路  5Comments  路  Source: Semantic-Org/Semantic-UI-React

Bug Report

Steps

Create a Dropdown with content, for example like in https://react.semantic-ui.com/modules/dropdown#usage-item-content. The content must contain a React component, for example:

content: <Header icon='desktop' content='Desktop' subheader='The largest size' />,

Expected Result

App should run normally, display the header, update the dropdown as needed, etc.

Actual Result

When Dropdown's componentWillReceiveProps runs, the app freezes for a minute or more. The issue is in line https://github.com/Semantic-Org/Semantic-UI-React/blob/972b999b7e9f85fb6722a1749c4c121c03e5613d/src/modules/Dropdown/Dropdown.js#L426. The deep equals recurses into the Header object, which has cyclical references (for example nextProps.options[0].content._owner === nextProps.options[0].content._owner.return.child) as well as recursive references through the entire app treee (like _owner._debugOwner._debugOwner._debugOwner... all the way to the root) and thus the app hangs.

The code in question came in via https://github.com/Semantic-Org/Semantic-UI-React/pull/2252, where performance risks of a deep equal were already mentioned.

Version

0.82.0

Testcase

The bug won't reproduce in a small sample app, because in that case lodash.isEqual can traverse the entire app quickly. You'll need a fairly large app to get a freeze of several seconds and it gets worse in debug mode, because then React components have the _debugOwner property set, which can be followed all the way to the root component.

RFC

Most helpful comment

@levithomason I agree, a falsely skipped property is worse than recursing into one too many. I was thinking of a blacklist approach, i.e. use a heuristic to determine if the current object is a React component (i.e. it has a render function, maybe some other properties or functions) and then blacklist certain properties (maybe only _debugOwner if things are speedy after that) and then see how far that gets us. If people are generally ok with such an approach, I'll try to find time this week to make a PR. Let me know!

All 5 comments

馃憢 Thanks for opening your first issue here! If you're reporting a 馃悶 bug, please make sure you've completed all the fields in the issue template so we can best help.

We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can.

This will be not easy to fix, see #2252.

@levithomason @layershifter What about a deep equals that does not recurse into react components, or at least not into their debug properties like _debugOwner, would that be an acceptable compromise?

We just need to be sure that changes to nested properties within options will be considered a change and update the component.

We could add our own lib util for doing equality checks. It could ignore private React properties. Thoughts?

@levithomason I agree, a falsely skipped property is worse than recursing into one too many. I was thinking of a blacklist approach, i.e. use a heuristic to determine if the current object is a React component (i.e. it has a render function, maybe some other properties or functions) and then blacklist certain properties (maybe only _debugOwner if things are speedy after that) and then see how far that gets us. If people are generally ok with such an approach, I'll try to find time this week to make a PR. Let me know!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

keeslinp picture keeslinp  路  3Comments

mattmacpherson picture mattmacpherson  路  3Comments

nix1 picture nix1  路  3Comments

devsli picture devsli  路  3Comments

KevinGorjan picture KevinGorjan  路  3Comments