styled-components / styled-components

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅
https://styled-components.com
MIT License
40.56k stars 2.5k forks source link

The component id is messed up when use with react-loadable at server-side rendering. #2831

Closed acrazing closed 5 years ago

acrazing commented 5 years ago

At first: we have 3 async components which would be loaded by react-loadable, we call it as c1, c2, c3. at server-side, react-loadable load all the components in order, but we only used 2 of them(c1,c3) at initial render, which means thec2is loaded afterc3`, and then, the component id is messed up!

Example code:

  1. c1, c2, c3:

    // SCAndRL.m1.tsx
    import * as React from 'react';
    import { PureComponent } from 'react';
    import styled from 'styled-components';
    
    const M1Root = styled.div`
      content: 'm1';
    `;
    
    export class SCAndRLM1 extends PureComponent {
      render() {
        return <M1Root>Content from m1.</M1Root>;
      }
    }
    
    export default SCAndRLM1;
    
    console.log('m1 loaded');
    
    // SCAndRL.m2.tsx
    import * as React from 'react';
    import { PureComponent } from 'react';
    import styled from 'styled-components';
    
    const M2Root = styled.div`
      content: 'm2';
    `;
    
    export class SCAndRLM2 extends PureComponent {
      render() {
        return <M2Root>Content from m2.</M2Root>;
      }
    }
    
    export default SCAndRLM2;
    
    console.log('m2 loaded');
    
    // SCAndRL.m3.tsx
    import * as React from 'react';
    import { PureComponent } from 'react';
    import styled from 'styled-components';
    
    const M3Root = styled.div`
      content: 'M3';
    `;
    
    export class SCAndRLM3 extends PureComponent {
      render() {
        return <M3Root>Content from M3.</M3Root>;
      }
    }
    
    export default SCAndRLM3;
    
    console.log('M3 loaded');
  2. entry component:

    import * as React from 'react';
    import { PureComponent } from 'react';
    import Loadable from 'react-loadable';
    
    const SCAndRLM1 = Loadable({
      loader: async () => import(/* webpackChunkName: 'm1' */ './SCAndRL.m1'),
      modules: [require.resolveWeak('./SCAndRL.m1')] as string[],
      webpack: () => [require.resolveWeak('./SCAndRL.m1')] as string[],
      loading: () => null,
    });
    
    const SCAndRLM2 = Loadable({
      loader: () => import(/* webpackChunkName: 'm2' */ './SCAndRL.m2'),
      modules: [require.resolveWeak('./SCAndRL.m2')] as string[],
      webpack: () => [require.resolveWeak('./SCAndRL.m2')] as string[],
      loading: () => null,
    });
    
    const SCAndRLM3 = Loadable({
      loader: () => import(/* webpackChunkName: 'm3' */ './SCAndRL.m3'),
      modules: [require.resolveWeak('./SCAndRL.m3')] as string[],
      webpack: () => [require.resolveWeak('./SCAndRL.m3')] as string[],
      loading: () => null,
    });
    
    export class SCAndRLEntry extends PureComponent {
      state = { isLoaded: false };
    
      render() {
        return (
          <div>
            <SCAndRLM1 />
            {this.state.isLoaded && <SCAndRLM2 />}
            <SCAndRLM3 />
          </div>
        );
      }
    }
  3. server side code:

    (req, res) => {
      const modules = new Set<string>();
      req.data.content = renderToString(
        <Loadable.Capture report={(module) => {
          console.log('report module', module);
          modules.add(module)
        }}>
          {withRouter(req, SCAndRLEntry)}
        </Loadable.Capture>,
      );
      resolveLoadableResource(req, modules);
      res.end(req.render());
    }

We called await Loadable.preloadAll() before listen in server side and await Loadable.preloadReady() before hydrate in client side. Just as you would expect, server side logs

M3 loaded
m2 loaded
m1 loaded

in order, and client side logs:

M3 loaded
M1 loaded

And then, the class name of M3Root becomes the class name of M2Root, react throws the warning:

Warning: Prop `className` did not match. Server: "sc-EHOje fGYMwV" Client: "sc-ifAKCX ciyLiT"
acrazing commented 5 years ago

I see what the problem is.

KevinOl commented 4 years ago

Would be nice knowing what you saw mate. Just so people who also got this problem can see too ;-)

acrazing commented 4 years ago

@KevinOl You need to use babel-plugin-styled-components or typescript-plugin-styled-components or typescript-styled-components-plugin to generate static componentId to avoid mangle id.