Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scripts don't work when I use getScriptTags().replace(/async/gi, 'defer'); hackaround #685

Closed
SkipEveryLunch opened this issue Jan 1, 2021 · 5 comments
Labels

Comments

@SkipEveryLunch
Copy link

SkipEveryLunch commented Jan 1, 2021

🐛 Bug Report

Hi,
I'm trying "replacing async with defer" hackaround proposed in #653 to use loadable-components in conjunction with webpack 5.

The only know method for webpack5 is to replace async scripts by defer scripts. Which is not supported by loadable itself, and you have to replace strings manually :(

Thank a lot. This hackaround banished all nasty error messages.
However, when I use this method, code-splitted components are rendered statically--in those components, all useState or useEffect gimmicks don't work, and the output of console.log is displayed only on server side.

To Reproduce

on server side,

const statsFile = path.resolve(__dirname, './loadable-stats.json');
const extractor = new ChunkExtractor({ statsFile });

//res.send() the output of the following function on express server
export default () => {

  const renderApp = () => {
    return <Home />;
  };

  const jsx = extractor.collectChunks(renderApp());
  const content = renderToString(jsx);

//extracted tags
  const linkTag = extractor.getLinkTags();
  const styleTag = extractor.getStyleTags();
//this is the hackaround!!!
  const scriptTags = extractor.getScriptTags().replace(/async/gi, 'defer');

  return `
  <html>
    <head>
      ${linkTag}
      ${styleTag}
    </head>
    <body>
      ${scriptTags}
      <div id="root">${content}</div>
      <script src="/bundle.js"></script>
    </body>
  </html>
`;
};

on client side,

import { loadableReady } from '@loadable/component';

loadableReady(() => {
  ReactDom.hydrate(<Home />, document.querySelector('#root'));
});
import loadable from '@loadable/component';
const TestLoadable = loadable(() => import('../components/TestLoad'));

const Home = () => {
  console.log("I'm displayed both on serverand client sides")
  return (
    <div>
      <h1>Hello world</h1>
      <TestLoadable />
    </div>
  );
};
export default Home;
const TestLoadable = () => {
  console.log("I'm displayed only on server side...")
  const [number, setNumber] = useState(0);
  return (
    <div>
      <h1>{number}</h1>
//this button doesn't increment the number
      <button onClick={() => setNumber(number + 1)}>Increment</button>
    </div>
  );
};
export default TestLoadable;

The above codes render like this;
screenshot
You'd think you could increase the number by clicking the button, wouldn't you?
Actually you couldn't...
I'd appreciate it if someone'd shed some lights on it.

Expected behavior

Script tags work in component
(The output of console.log() is displayed both on server and client sides,
Tne number increases when the button is clicked)

Link to repl or repo (highly encouraged)

https://github.com/SkipEveryLunch/loadable-test

Run npx envinfo --system --binaries --npmPackages @loadable/component,@loadable/server,@loadable/webpack-plugin,@loadable/babel-plugin --markdown --clipboard

The version of webpack is 5.4.0

## System:
 - OS: Linux 4.19 Ubuntu 18.04.4 LTS (Bionic Beaver)
 - CPU: (4) x64 Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz
 - Memory: 6.02 GB / 7.70 GB
 - Container: Yes
 - Shell: 4.4.20 - /bin/bash
## Binaries:
 - Node: 14.15.1 - ~/.nvm/versions/node/v14.15.1/bin/node
 - Yarn: 1.22.5 - /usr/bin/yarn
 - npm: 6.14.10 - ~/.nvm/versions/node/v14.15.1/bin/npm
## npmPackages:
 - @loadable/babel-plugin: ^5.13.2 => 5.13.2 
 - @loadable/component: ^5.14.1 => 5.14.1 
 - @loadable/server: ^5.14.0 => 5.14.0 
 - @loadable/webpack-plugin: ^5.14.0 => 5.14.0 
@theKashey
Copy link
Collaborator

There is opened PR with webpack 5 related fix - #676
should be merged in a few days, happy new year.

@SkipEveryLunch
Copy link
Author

SkipEveryLunch commented Jan 4, 2021

@theKashey Thank you for getting back to me.
After 3 days of madness, I found that scripts work if I import babel-polyfill in a code which calls loadableReady. like this;

import 'babel-polyfill'
import { loadableReady } from '@loadable/component';

loadableReady(() => {
  ReactDom.hydrate(<Home />, document.querySelector('#root'));
});

Which causes another nasty error though.

Uncaught Error: only one instance of babel-polyfill is allowed

I will test if there is an alternative for babel-polyfill (yeah, I read it was now deprecated) and if #676 fix helps it.
Anyway, thank you for your answer and happy new year.

@theKashey
Copy link
Collaborator

polyfills should be imported before everything, however, importing them manually is not a good idea - babel can inject required ones by itself (not covering polyfills required for node_modules)

@stale
Copy link

stale bot commented Mar 7, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Mar 7, 2021
@stale stale bot closed this as completed Mar 16, 2021
@vladislav-koval
Copy link

Hi, I have the same problem, did you manage to solve this error?
Uncaught Error: only one instance of babel-polyfill is allowed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants