ctrlplusb / react-universally

A starter kit for universal react applications.
MIT License
1.7k stars 244 forks source link

styled-components improvement #297

Open datoml opened 7 years ago

datoml commented 7 years ago

Hello,

I made some tests with server side rendering and the styled components feature branch. Seems like the server side rendering doesn't work for the styles. Regarding to this discussions https://github.com/styled-components/styled-components/issues/124 I implemented this:

file: src/server/middleware/reactApplication/index.js

import styleSheet from 'styled-components/lib/models/StyleSheet'
...
// Create our React application and render it into a string.
  const reactAppString = renderToString(
    <CodeSplitProvider context={codeSplitContext}>
      <ServerRouter location={request.url} context={reactRouterContext}>
        <DemoApp />
      </ServerRouter>
    </CodeSplitProvider>,
  );

  const styles = styleSheet.rules().map(rule => rule.cssText).join('\n')

  // Generate the html response.
  const html = generateHTML({
    // Provide the full app react element.
    reactAppString,
    // Nonce which allows us to safely declare inline scripts.
    nonce,
    // Running this gets all the helmet properties (e.g. headers/scripts/title etc)
    // that need to be included within our html.  It's based on the rendered app.
    // @see https://github.com/nfl/react-helmet
    helmet: Helmet.rewind(),
    // We provide our code split state so that it can be included within the
    // html, and then the client bundle can use this data to know which chunks/
    // modules need to be rehydrated prior to the application being rendered.
    codeSplitState: codeSplitContext.getState(),
    styledComponents: styles,
  });

Then I added them into

file: src/server/middleware/reactApplication/generateHTML.js
export default function generateHTML(args: Args) {
  const { reactAppString, initialState, nonce, helmet, codeSplitState, styledComponents } = args;
  ...
return `<!DOCTYPE html>
    <html ${helmet ? helmet.htmlAttributes.toString() : ''}>
      <head>
        ${helmet ? helmet.title.toString() : ''}
        ${helmet ? helmet.meta.toString() : ''}
        ${helmet ? helmet.link.toString() : ''}
        ${styleTags(assetsForRender.css)}
        <style>${styledComponents || ''}</style>
        ${helmet ? helmet.style.toString() : ''}
      </head>
      <body>

Looks now a lot nicer to me. Can someone confirm that this is a good solution? :) Thanks

codepunkt commented 7 years ago

It's a start. More information on styled-components SSR @ https://github.com/styled-components/styled-components/pull/214

ctrlplusb commented 7 years ago

Thanks for looking into this @datoml :)

I'll have a review of this soon. :)

teonik commented 7 years ago

The glamor styleSheet that styled-components exposes is a singleton.With the above approach every request to render a page will result in it's styles being appended to it.So in subsequent requests you end up sending previously rendered CSS unrelated to the current page.

Calling styleSheet.flush() in between requests doesn't seem to work at the moment.

ctrlplusb commented 7 years ago

Has someone got a full working implementation that they are willing to create a PR for?

datoml commented 7 years ago

I am currently using styled-components for my project with the hope that there will be a fix in die near future for this. @ctrlplusb I am not that familiar with creating a PRs :).

ctrlplusb commented 7 years ago

@datoml I encourage you to try!

Check out this cool egghead course: How to Contribute to an Open Source Project on GitHub

datoml commented 7 years ago

Thanks for the link. I'll have a look :):

rlindskog commented 7 years ago

@ctrlplusb I implemented the SSR changed that @datoml recommended above, you can check it out here. It seems to work, but it double injects the styles, once on the server and once on the client.

codepunkt commented 7 years ago

I'm wondering if you read what i linked. That's all known and discussed and there's no solution for it yet, even though it's very simple in principle https://github.com/styled-components/styled-components/pull/214

datoml commented 7 years ago

I did. We currently have to wait that this gets implemented. Everything here is currently a workaround until its done and gets released.

ctrlplusb commented 7 years ago

FYI I have been using styletron, which has no problems with SSR and has a similar API to styled-components. In case this is blocking someones adoption, there are alternatives.

datoml commented 7 years ago

I tried styletron but I like the api from styled-components more. Doing something like this feels awesome to me :).

import styled from 'styled-components';

const button = styled.button`
  color: green;
  border: 1px solid blue;
`;
codepunkt commented 7 years ago

I'm hard to convince as well. On the one hand i'm not sure about the performance implications of styletrons many CSS selectors in really large DOMs (think Githubs Diff view), on the other hand i love to be able to write CSS instead of objects, including inline syntax highlighting etc, so i'm with @datoml on that one :)

ctrlplusb commented 7 years ago

Totally see your guys points, I guess I prefer the object style as it opens up the Javascript as the API to modifying/merging styles:

const centeredButtonStyle = Object.assign(
  {}, 
  { color: 'red' },
  theme.layout.centered
); 

I have been really enjoying creating and composing objects for more generic styles.

datoml commented 7 years ago

Thats true. The reason styled-components won my heart was that I can copy the whole css and go back to good old css styles. Every approach has it pros and cons tho :).

ctrlplusb commented 7 years ago

Check this out: https://github.com/RafalFilipek/styled-props

:)

datoml commented 7 years ago

I will have a look on it :). But it seems similar to the build in ThemeProvider. https://github.com/styled-components/styled-components#theming

Or am I missing something? :D

teonik commented 7 years ago

@codepunkt Thanks for suggesting styletron, i was totally unaware of it's existence....:P

lucianlature commented 7 years ago

@ctrlplusb @codepunkt Server Rendering API for styled-components has just been merged into master

codepunkt commented 7 years ago

@lucianlature it was merged into v2 branch, not master.

lucianlature commented 7 years ago

Got too excited for a moment, sorry.

bkniffler commented 7 years ago

Out of curiosity, did you guys look at https://github.com/rofrischmann/fela According to https://github.com/hellofresh/css-in-js-perf-tests it is extremely fast and creates very small sized output compared to something like glamor/styletron. The docs are great (http://fela.js.org/docs/guides/UsageWithReact.html), it works with react-native, and SSR is implemented and works too.

And with https://github.com/jakecoxon/babel-plugin-css-to-js you can have a very similar API to styled-components!

codepunkt commented 7 years ago

@bkniffler The alternatives are endless. CSS in JSS fatigue! 🤣

mschipperheyn commented 7 years ago

In order to get styled-components working server-side with current v2-beta, which api seems still a bit under discussion (https://github.com/styled-components/styled-components/pull/214), you can use this approach.

https://gist.github.com/mschipperheyn/c17280278218074a53147f54259af66a

The reference keyword is: styledComponentCSS

I based this on react-universally@next

designspin commented 7 years ago

Does anybody have styled components working with react universally nowadays? I am using version 2.1.1 of styled components, but have not had any joy. I looked at the gist provided by 'mschipperheyn', but it looks like things have changed in styled-components land!

I am trying to use ServerStyleSheet, creating an instance and then using collectStyles on my app component. But I am missing something, getStyleElement returns an empty array. I know that app is async so perhaps at the point of collection it is empty?

designspin commented 7 years ago

Okay, I was on the right track. I managed to implement Styled Components by passing my ServerStyleSheet instance as a prop on ServerHTML, then using .getStyleElement() to add the css to headerElements within ServerHTML.js. I am really pleased I got this working and also pleased I found React Universally.

oyeanuj commented 7 years ago

@designspin is there a gist you could share if it all works? Btw, here is the main issue on styled-components where you could also maybe post that update for others following the issue - https://github.com/styled-components/styled-components/issues/762

designspin commented 7 years ago

@oyeanuj

Here is a GIST of what appears to be working for me right now: https://gist.github.com/designspin/c11095334ae1f105d1f93123232d37fd

strues commented 7 years ago

@designspin If you will link me a repo where you're having issues, I'll take a look.

designspin commented 7 years ago

@strues

No issue, the above GIST is in fact a solution. It is working for me, thanks.

oyeanuj commented 7 years ago

@mschipperheyn @strues @designspin Does anyone have the setup for using the starter kit with SC v2? The feature branch still seems to have v1 of SC.