KingSora / OverlayScrollbars

A javascript scrollbar plugin that hides the native scrollbars, provides custom styleable overlay scrollbars, and preserves the native functionality and feel.
https://kingsora.github.io/OverlayScrollbars
MIT License
3.78k stars 214 forks source link

[react] Scrollable component renders twice as much as it should #558

Closed crhistianramirez closed 12 months ago

crhistianramirez commented 12 months ago

Describe the bug The component for which the overlay scrollbars is applied to is rendered twice as many times as it should

To Reproduce Steps to reproduce the behavior:

  1. Go to codesandbox example
  2. Open developer console
  3. Observe that the App is rendered twice (expected in development environment)
  4. Observe that the List component is rendered four times

Expected behavior I would expect the List to render no more than it's parent component

Examples https://codesandbox.io/p/sandbox/happy-surf-l4vszs?file=/package.json:14,6-14,29

Environment

Additional context

KingSora commented 12 months ago

Good day @crhistianramirez :)

If you remove reacts StrictMode the App is rendered once and the List is rendered twice. This behavior is actually intended because of how react (and SSR) works.

The initial renders markup is supposed to be:

<div data-overlayscrollbars-initialize="">{content}</div>

This is also send over the network in case of SSR.

When the document hydrates (Javascript kicks in) the second render happens and the markup becomes:

<div data-overlayscrollbars-initialize="">
  <div>
    {content}
  </div>
</div>

This markup is then used to initialize the OverlayScrollbars library.

crhistianramirez commented 12 months ago

Hi @KingSora appreciate the quick reply :)

I'm not well versed in SSR so forgive my ignorance but are you saying that even though this component isn't being rendered on the server it needs to render twice in order to support the SSR use case?

KingSora commented 12 months ago

@crhistianramirez Basically yes.. on the client the component doesn't know whether its hydrating or really rendering for the first time, so I have to always treat it as if it would render on the server and then hydrate on the client, even if it does render only on the client.

I would say that this behavior shouldn't influence anything in your app since its only happening at the beginning of the lifecycle (its rendering twice in the beginning but after that its rendering normaly (once) on every subsequent update). So the performance impact should be very minimal.

crhistianramirez commented 12 months ago

Got it, thanks for the context, I'll close this issue then

ignatevdev commented 11 months ago

Is it possible to make this behaviour optional?

In my case it causes the child component to mount twice, which can cause issues in cases where you rely on lifecycle to do some actions, for example if you want to load data on mount, this would trigger loading twice.

In my opinion SSR support is important, but it's usage is relatively small compared to react as SPA, therefore it makes sense to add a flag such as ssr={true}, which would enable the current behaviour, but disable it by default.

Also I'm not sure if there's certain logic behind avoiding an extra div during SSR, but if it's made only to save a few bytes, I think it's not worth it.

KingSora commented 11 months ago

@ignatevdev good day :)

Are you absolutely sure that OverlayScrollbars causes your child component to mount twice?

This behavior is compareable to a state change, which really shouldn't cause a re-mount of any sorts. So having any kind of state in your component is the same thing. - If that causes bugs its possible that your implementation is missing something.

If you run react in dev environment it uses its StrictMode to mount components twice to find bugs or undesired behavior in your code.

In case you think I'm missing something here, please do post an example of your code so I can look into your issue :)

ignatevdev commented 11 months ago

@KingSora Thank you for a quick reply!

I've made a sandbox with a reproduction of this problem, if you look at the log you will see that the child component is mounted twice.

In my knowledge this happens when you change the hierarchy of the components, in this case when you add an additional div wrapping the children on the second render.

https://codesandbox.io/s/youthful-brook-lgrmlg?file=/src/App.js

KingSora commented 11 months ago

@ignatevdev On the first sight in seems like my suspicion with reacts StrictMode was correct.. I forked your sandbox and removed the <StrictMode> component from the index.js which removes the second log: https://codesandbox.io/s/compassionate-browser-cz4j8k?file=/src/index.js

As a second "prove" I've switched out the Scrollbar component with a div, but I've kept the <StrictMode> component which also gives me two logs: https://codesandbox.io/s/reverent-worker-zt3sqw?file=/src/App.js proving that multiple logs doesn't result because of the Scrollbar component

ignatevdev commented 11 months ago

You are right, in this case StrictMode does cause the issue, however I'm not using StrictMode in my app, but the issue persists.

Since this issue is already closed, I'll try to find the cause and come back if I'll succeed.

Thank you for your help!