ctrlplusb / react-universally

A starter kit for universal react applications.
MIT License
1.69k stars 243 forks source link

[FAQ] flash of unstyled content #322

Open bradennapier opened 7 years ago

bradennapier commented 7 years ago

May not be worth adding since I believe this should only happen during development - but it does annoy me quite a bit. It could be worth looking into implementing a solution similar to what was done by universal-webpack

ctrlplusb commented 7 years ago

Understand it's annoying, but I don't want to introduce hacks around this.

Let's update the FAQ though.

elektronik2k5 commented 7 years ago

How about using something like isomorphic-style-loader, which is used in react-starter-kit? I'd happily work on a PR, but am not comfortable with isomorphic apps enough to give it a shot just yet. Can anyone assess this approach? Will it work well? Is it worth to try? @ctrlplusb

strues commented 7 years ago

Something like this in the HTML component will lessen the effect, if not solve it completely.

        ${
          _.keys(assets.css).length === 0 ?
            `<style>${
              // $FlowIssue
              require('../styles/normalize.css')._style +
              // $FlowIssue
              require('../components/App/styles.scss')._style +
              // $FlowIssue
              require('../components/Home/styles.scss')._style +
              // $FlowIssue
              require('../components/NotFound/styles.scss')._style
            }</style>` : ''
        }
birkir commented 7 years ago

Hard-coding css file imports is not a solution IMO.

You need to enable extract-text-plugin on the server end, something along these lines:

// ./internal/webpack/configFactory.js

// in plugins

ifElse(isDev && isServer)(
  () => new ExtractTextPlugin({
    filename: 'dev-styles.css', allChunks: true,
  }),
),

// in module.rules css section

ifElse(isDev && isServer)(() => ({
  loader: ExtractTextPlugin.extract({
    fallback: 'style-loader',
    use: ['css-loader/locals'], // You have to use locals on the server (no window).
  }),
})),

// ./server/middleware/reactApplication/ServerHTML.js
const headerElements = removeNil([
  // ...
  onlyIf(process.env.BUILD_FLAG_IS_DEV, () => stylesheetTag('/dev-styles.css')),
]);
elektronik2k5 commented 7 years ago

@birkir @strues @ctrlplusb can anyone more knowledgeable than me please have a look at my suggestion above to solve this without reinventing the wheel? :wheel_of_dharma:

birkir commented 7 years ago

I'm not a fan of isomorphic style loader HOC s method so i cannot really comment on the issue. If what we already have can be configured to work rather than bringing in another dependency, I would rather support that :)

Did the above solution not work for you?

strues commented 7 years ago

I'm probably going to present an unpopular opinion, but I've basically grown accustomed to the FOUC. It's been so common that I've accepted it as part of the game. I do know you can avoid it by adding BrowserSync to the mix, but that itself can be another headache.

elektronik2k5 commented 7 years ago

@birkir,

I'm not a fan of isomorphic style loader HOC s method

Can you please elaborate? I haven't tried using it myself, so don't have an informed opinion :(. I just saw it is in use in the very popular react-starter-kit universal boilerplate and thought we can use it to solve the same issue.

birkir commented 7 years ago

@strues Same for me and other devs at Ueno., we actually like the flash of unstyled content. We have had some issues when we're calculating element dimensions in componentDidMount with something like getBoundingClientRect, as the styles have not loaded so we get incorrect dimensions. So we are forced to think about this, as this will happen with clients with slow network as well.

@elektronik2k5 In components that will be using styles you have to add higher order component (export default withStyles(styles)(MyComponent)). I am fine with HOC's in general for special occasions, but every damn component is brutal if you think about that the below example will fail:

// Demo.js
import s from './Demo.css';

class Demo {
  render() {
    return <div className={s.foo}>Node 1</div>;
  }
}
export default withStyles(s)(Demo);

// Demo.spec.js
test('Demo component has div.foo', () => {
  const component = shallow(<Demo />);
  expect(component.childAt(0).equals(<div className={s.foo} />)).to.equal(true);
});

Some will be fine with using contains, find, etc. This is just my personal opinion.

oyeanuj commented 7 years ago

Wondering if anyone here has figured out a solution to this FoUC yet?

@ctrlplusb Would you consider hiding the DOM node with the app until style has loaded in 'development' mode a hack? 😄

strues commented 7 years ago

Accept that it happens in development. 9.9999/10 if will not happen in production. Most of the time during development the cause is styled-components or a Webpack loader. Once the styles are compiled and served, you wont be experiencing it.

Alternatively, here's a solution:

        ${
          _.keys(assets.css).length === 0 ?
            `<style>${
              // $FlowIssue
              require('../styles/normalize.css')._style +
              // $FlowIssue
              require('../components/App/styles.scss')._style +
              // $FlowIssue
              require('../components/Home/styles.scss')._style +
              // $FlowIssue
              require('../components/NotFound/styles.scss')._style
            }</style>` : ''
        }

Load each and every individual style 👎 .

oyeanuj commented 7 years ago

@strues It seems like it shouldn't be happening with styled-components according to these comments in that repo?

oyeanuj commented 7 years ago

An update:

So, I've fixed the FoUC with Styled-Component as part of #505. It turns out the SC styles were never being rendered on the server, only the client. With that fixed, FoUC from SC is gone in that feature branch. Check out https://github.com/ctrlplusb/react-universally/pull/505

Now that leaves three other sources of FoUC - Normalize.css, Globals.css, and Milligram.

For the SC feature branch: The first two of these can be fixed by using Polished (or other similar styled-components normalize/reset libraries), and using injectGlobal for globals.css. Solution forMilligrammight also beinjectGlobal`.

Not sure about solutions in the master branch.