react-select icon indicating copy to clipboard operation
react-select copied to clipboard

Some css styles are not created in production but work in development

Open jjlauer opened this issue 6 years ago • 41 comments

Hello,

We are hitting an odd css/emotion issue in production, while the component works great in development. We've tried this on v2.0.0-v.2.2.0 and its the same issue with all of them.

The dynamic css rules for the outer divs never get created in the emotion stylesheet. They are, however, set on the divs in the DOM. The rest of the dynamic css rules (e.g. on the options when you click on the control) do get created in the emotion stylesheet. This only happens on a few pages in our app, while other parts work as expected. Here is a screenshot of the the DOM when the page loads.

screenshot from 2019-01-01 19-22-05

You'll notice that the select outermost div was assigned a classname of css-10nd86i, but that css rule does not actually exist in the DOM. Using the javascript console to dump out the emotion stylesheet, you'll notice in this screenshot that the rules are totally empty in the stylesheet.

screenshot from 2019-01-01 19-23-24

Now if you click on the select control to open it, the css rules for the options list all get created:

screenshot from 2019-01-01 19-43-12

So the options get styled correctly, but the primary select component does not. I've tried various versions of emotion, react-select, react, etc., but the issue still persists. We've had to swap out this component in production for the time being, but would like to figure this out. Any help would be greatly appreciated.

jjlauer avatar Jan 02 '19 00:01 jjlauer

@jjlauer are there any key similarities between the pages where this happens? Any information you could provide us to narrow down a reproducible case for this would be helpful; and if possible a codesandbox reproduction.

This usually happens when you're trying to instantiate emotion across contexts, like in an iframe or through server side rendering for example.

gwyneplaine avatar Jan 02 '19 05:01 gwyneplaine

Made some headway. On the page in our app that works, it's a very complicated page and the user would only see the Select component if they were already on the page for a few seconds (e.g. javascript all loaded, before Select ever rendered).

On the page this issue occurs the Select component is rendered immediately when the browser loads the page. If I delay the Select from being rendered for even just 100ms (via setTimeout) then it renders correctly. Any ideas on what could be causing something like that and why even just 100ms delay in React rendering the Select component would fix it?

jjlauer avatar Jan 04 '19 04:01 jjlauer

@jjlauer Did you ever figure this out? I'm having the same issue. The selector is properly styled in a development environment, but in a production build none of the emotion stylesheets get injected into the head.

ryanrombough avatar Apr 02 '19 17:04 ryanrombough

@ryanrombough Unfortunately no. Only workaround is what i mentioned above -- you have to delay rendering of the widget so it doesn't occur on the first react render().

jjlauer avatar Apr 02 '19 17:04 jjlauer

@jjlauer I just tried implementing a 500ms delay on rendering but it did not solve the issue for me. Guess I'll keep digging. Thanks!

ryanrombough avatar Apr 02 '19 18:04 ryanrombough

I have narrowed the cause of my issue down to the minification step in my Webpack v4 build. Here's the stack overflow question I created to try to find out why this is happening.

ryanrombough avatar Apr 03 '19 22:04 ryanrombough

Hmm.. did turning off minimize in webpack fix your production issue?

I also had issues w/ the css not being injected into the header, but adding a delay (of any use of react-select) on the initial react render fixed it. It feels like a race condition between Emotion fully initializing before react-select uses it.

On Wed, Apr 3, 2019 at 6:10 PM Ryan Rombough [email protected] wrote:

I have narrowed the cause of my issue down to the minification step in my Webpack v4 build. Here's the stack overflow question https://stackoverflow.com/questions/55505056/why-would-webpack-4-minification-prevent-styles-for-react-select-component .

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/JedWatson/react-select/issues/3309#issuecomment-479677022, or mute the thread https://github.com/notifications/unsubscribe-auth/AAjwAtXg0d-2BhpFcJqVxX0oGVuaiV4Dks5vdSbOgaJpZM4ZmF0W .

jjlauer avatar Apr 03 '19 22:04 jjlauer

Yea, but that's not really a solution for us because our bundle is too large when not minified. I tried implementing the delay you suggested but could not get it to work.

ryanrombough avatar Apr 03 '19 22:04 ryanrombough

Hi @jjlauer I am facing the same CSS not being applied in Production. If you can kindly help me with the code snippet for setting the delay in rendering the react-select it would be extremely helpful!! Thank you.

ChayanJana1233 avatar Feb 23 '20 20:02 ChayanJana1233

I have the same issues

hemedani avatar May 17 '20 14:05 hemedani

I managed to fix the issue by adding classNamePrefix which seems to force react-select to add the classNames to the sub-components in production

<Select
  classNamePrefix='select'
  {...}
/>

Before

<div class=" css-1hwfws3">
  ...
</div>

After

<div class="select__value-container select__value-container--is-multi select__value-container--has-value css-1hwfws3">
  ...
</div>

NearHuscarl avatar Aug 23 '20 04:08 NearHuscarl

Thanks for letting us know @NearHuscarl!

bladey avatar Aug 24 '20 03:08 bladey

@NearHuscarl's solution didn't solve my problem. Have used classNamePrefix for a while, and the issue remains the same.

@jjlauer how did you implement the delay?

@bladey is there a way to "force" the CSS to be loaded programatically?

Getting desperate here 😅

kunambi avatar Oct 22 '20 20:10 kunambi

Here is how I implemented a very simple delay to rendering. Here's the main react component (e.g. top-level entrypoint). I originally was rendering the ReportsPanel component, but developed a simple wrapper component i called DelayedRender that is now responsible for rendering it instead.

class ReportsPage extends Component {

  constructor() {
    super();
  }

  render() {
    const { dispatch, query, user, accounts } = this.props;

    return (
      <DelayedRender>
        <ReportsPanel
          dispatch={dispatch}
          query={query}
          user={user}
          accounts={accounts}
          active={true}
        />
      </DelayedRender>
    );
  }
}

Here is DelayedRender:

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';

export default class DelayedRender extends PureComponent {

  constructor() {
    super();
    
    this.state = {
      render: false
    };
  }
  
  componentDidMount() {
    const { time } = this.props;
    
    setTimeout(() => this.setState({ render: true }), (time || 400));
  }
  
  render() {
    const { children } = this.props;
    const { render } = this.state;
    
    if (!render) {
      return null;
    }
    
    return children;
  }
}

jjlauer avatar Oct 22 '20 20:10 jjlauer

This seems like it's still present in [email protected].

wegry avatar Mar 05 '21 20:03 wegry

This problem has reappeared for us in 4.2.1

rushilsrivastava avatar Mar 09 '21 02:03 rushilsrivastava

@rushilsrivastava are you folks using react-windowed-select as well as react-select? My team has a hunch it may be the former causing issues.

Confusingly, downgrading just react-windowed-select to use react-select 3 seems to resolve the issue.

wegry avatar Mar 09 '21 11:03 wegry

@wegry We aren't using react-windowed-select, still having a hard time pinpointing what package/code change caused this since downgrading to 4.1.0 doesn't seem to work for us either. We will give react-select v3 a shot though and report back here.

For the time being, we are using @jjlauer's solution.

rushilsrivastava avatar Mar 10 '21 01:03 rushilsrivastava

Greetings @wegry , @rushilsrivastava , @jjlauer , or anyone else,

Would any of you be willing to share your package.json to help us recreate the issue?

ebonow avatar Mar 11 '21 23:03 ebonow

@wegry Can you look at your yarn.lock or package-lock.json and see if anything depends on Emotion besides react-select?

Methuselah96 avatar Mar 12 '21 14:03 Methuselah96

@wegry I looked into your package.json and it looks like you have react-windowed-select@^2.0.2 installed which references react-select@3. Can you try upgrading that to [email protected] and see if it makes a difference? react-windowed-select@2 depends on react-select@3, but react-windowed-select@3 depends on react-select@4, so that mismatch could be a potential cause of the issue.

Methuselah96 avatar Mar 12 '21 14:03 Methuselah96

@Methuselah96 let me post my whole package.json and yarn.lock as a gist. The version above is current after the downgrade. I'll send the version reproing this instead. My mistake.

wegry avatar Mar 12 '21 14:03 wegry

Just so it's clear that I added a gist (not sure if edits notify), https://gist.github.com/wegry/5b2d97db8257b459b95c08c1e3153dd5. @Methuselah96

wegry avatar Mar 17 '21 13:03 wegry

@wegry Yeah, I'm not seeing anything suspicious. At this point the only thing to do is to try to recreate the issue in a public repo so that we can dig into it more. Are you using Webpack? Are there CSS loaders that you're using that could be affecting this?

Methuselah96 avatar Mar 19 '21 15:03 Methuselah96

@Methuselah96 I've unfortunately been unable to repro this at a smaller scale than the app it's occuring in.

Are you using Webpack?

Yeah, [email protected].

I'm thinking in our case, it's because we have two+ chunks that pull in react-select. The way we repro the styles visibly being broken (and not present in the dom) is

  1. Prod build
  2. cache free refreshing chunk A where we import react-select's default export.
  3. Navigating to a page (in a different chunk) where a wrapped component containing three different versions of react-select (one of which is 'react-select/creatable') and react-windowed-select is used.

There might be multiple emotion caches being used in our case?

Are there CSS loaders that you're using that could be affecting this?

I would think CSS loaders wouldn't affect this, especially since react-select doesn't package css, right?

wegry avatar Mar 21 '21 21:03 wegry

I had the issue with the styles not working with production build after some of my components were inserted into a shadow root. What worked for me was to add CacheProvider from emotion somewhere in the application root.

Please see https://github.com/JedWatson/react-select/issues/3680#issuecomment-809518245 for more details.

Hope this helps.

erkez avatar Mar 29 '21 16:03 erkez

Any update or solution on this issue? Apart from delay rendering

piotrrussw avatar Apr 21 '21 08:04 piotrrussw

I had this issue with react-select 4.3.0, and @NearHuscarl 's solution https://github.com/JedWatson/react-select/issues/3309#issuecomment-678728542 worked for me!

stack:

  • gatsby (3.4.1)
  • react (11.4.0)
  • emotion (gatsby-plugin-emotion: 6.5.0)
  • twin.macro (2.4.1)
  • react-select (4.3.0)

issue:

  • a route page (/myRoute) in my app contained a react-select component
  • on other pages, hovering on gatsby links <Link to="/myRoute"> caused the styles of unrelated components on the other pages to break.
    • this only happened on production builds. in the inspector, I could see that the links to /myRoute caused chunk load errors on hover.
    • likely due to gatsby's behavior of chunk splitting in prod builds, and pre-fetching chunks when hovering links.

solution:

  • adding a classNamePrefix="react-select" to our <Select component made the other pages' pre-fetched loaded css chunks not fail.

arda- avatar May 18 '21 22:05 arda-

I was able to come up with a simple workaround, but can only guess as to why it works.

TLDR I hid a <Select> in the _app.tsx outside of the <Component>.

// _app.tsx
function MyApp({Component, pageProps}: AppProps) {
    return (
        <>
        <div>
            // This is the User Context Provider from auth0
            <UserProvider>
                <div className="app-container">
                    <Component {...pageProps} />
                </div>
            </UserProvider>

            // Hide this select from view
            <div style={{display: "none"}}>

                // This select is put here to get the emotion styles rendered in production */}
                <Select
                    instanceId={"rendered-select"}
                    value={{label: "none", value: "none"}}
                    options={[{label: "none", value: "none"}]}
                    onChange={() => null}
                />
            </div>
        </div>
        </>
    )
}

export default MyApp

My guess as to why this works I had this issue running react-select 4.3.1 on next.js and tried the previously mentioned solutions but they did not work (delayed rendering https://github.com/JedWatson/react-select/issues/3309#issuecomment-714758497 worked occasionally, but not every time).

Since the delayed rendering worked occasionally, I thought it could be a caching issue (or some other race condition related to the inner workings of react/next.

I am using auth0 and wrapping every page in the UserProvider from auth0 in my _app.tsx. UserProvider then provides context via a useUser.

// Every page needing app user context uses this wrapper
const PageWrapper = ({children, serializedDsUser} : PageWrapperProps) => {
    // authUser is the auth0 user
    const { user: authUser, isLoading } = useUser()

    // dsUser is the app's user object
    const dsUser = serializedDsUser ? JSON.parse(serializedDsUser) : null

    const context: UserSessionContextValues = {
        authUser: authUser ? authUser : null,
        dsUser,
        isLoggedIn: authUser ? true : false
    }

    return (
        <div>
            {isLoading ?
                <AppLoader isLoading />
                :
                <UserSessionContext.Provider value={context}>
                {children}
                </UserSessionContext.Provider>
            }
        </div>
    )
}

export default PageWrapper

Each page does not render until user data returned by useUser completes loading. Without spending many more hours tracing this bug down, I'm guessing something having to do with conditionally rendering the pages and subsequently any nested <Select> components based on user state and SSR is causing some sort of race condition with the caching (as mentioned by @jjlauer https://github.com/JedWatson/react-select/issues/3309#issuecomment-479678164).

Hope this saves someone some time.

myorkgitis avatar Jul 20 '21 02:07 myorkgitis

None of the above with hiding in _app.tsx or setting a classname prefix works for me on nextjs currently on client route push. Has anyone been able to get this working recently?

delayed rendering doesnt work either i cant get the css to show up if im including the select conditionaly based on state

mattvb91 avatar Aug 24 '21 12:08 mattvb91