captivationsoftware/react-sticky

All events cause Sticky component to be remounted

Closed this issue · 5 comments

I have a bootstrap DropdownButton control in my Sticky component, which I am using for navigation. On the desktop, if I expand the DropdownButton and then scroll the page, the dropdown collapses. This is not normal behavior, but I can live with it.

What is worse is on mobile, if I expand the DropdownButton and then try to select one of the child MenuItems, the dropdown collapses without reflecting my selection. There is no way to expand the menu and then make a selection.

This happens because all events to which the Sticky Container subscribes result in a call to setState. This causes a rerender of the Sticky component, which in turn, calls React.cloneComponent on the child of the Sticky, which causes the child to be remounted.

Is there any known workaround for this?

I am planning to fork the library and only call setState when a property actually changes.

vcarl commented

I just set up a little CodeSandbox (https://codesandbox.io) and could not reproduce this behavior, the function being called does not re-mount the component being returned each time. Could you help me reproduce this?

https://codesandbox.io/s/0m16l89ymw

(It's a little bit broken, but it's logging to the CodeSandbox console. Open the console, reload the sandbox viewport from the little reload icon--NOT the browser reload--and scroll around. It mounts once and renders on every scroll event.)

vcarl commented

Closing this, because I can't reproduce. Can reopen if you provide a bug reproduction!

Sorry for the delay. Here is a repro: https://codesandbox.io/s/9qy5xky5y

If you open https://9qy5xky5y.codesandbox.io in Chrome, you can observe the behavior I described.

Expand Desktop Menu and then scroll the page down. The Dropdown Menu collapses because the Header component is remounted on scroll.

Open Chrome Dev Tools and toggle the Device toolbar (to emulate a mobile browser). Expand Mobile Menu then try to select Menu Item 1. The Header component is remounted on touch, so the menu item never receives the click.

Two things to point out:

  1. When I make the adjustments I mentioned in my post (change Sticky.js to only setState when a state property changes), the problem disappears.
  2. If I remove the Reactive components, the problem also disappears.

Given (2), the issue is obviously caused by how I have structured my component hierarchy, and not directly caused by react-sticky. I expect if I were to move the Reactive components up to the root level and have the Sticky components as children, it would work fine. I wish I understood why, though.

vcarl commented

Your reproduction is pulling in both react-responsive and react-bootstrap. When I remove those, it stops mounting. Looks like your bug is in another library.

Actually, I can see that react-sticky isn't causing this bug even without modifying it.

<Sticky>
  {({ style }) => (<Header style={style} />)}
</Sticky>

The only thing directly rendered by Sticky is Header, HeaderNav is rendered within Header. The console shows Header mounting once and HeaderNav mounting repeatedly, which means that something between Header and HeaderNav must be causing the thrashing.

vcarl commented

Ah I see I stopped reading prematurely, you note as much in your last paragraph. I'm not sure, and don't really have time to help you debug further. Maybe drop by Reactiflux (http://join.reactiflux.com), somebody might be able to help more.