facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
229.88k stars 47.07k forks source link

[DevTools Bug]: `gl.useProgram(program);` will cause react-refresh to perform incorrect code injection. #31279

Open Yukiniro opened 1 month ago

Yukiniro commented 1 month ago

Website or app

https://github.com/Yukiniro/reproduce/tree/master/rspack-issue-hot-update-worker

Repro steps

  1. clone https://github.com/Yukiniro/reproduce/tree/master/rspack-issue-hot-update-worker
  2. Run pnpm run dev
  3. Go to http://localhost:8080/
      const setup = () => {
        const offscreenCanvas = new OffscreenCanvas(100, 100);
        const gl = offscreenCanvas.getContext("webgl2");

        const program = gl.createProgram();
        gl.linkProgram(program);
        gl.useProgram(program);
      };
will be transform to be:
      var _s = $RefreshSig$();
      const setup = () => {
        _s();
        const offscreenCanvas = new OffscreenCanvas(100, 100);
        const gl = offscreenCanvas.getContext("webgl2");

        const program = gl.createProgram();
        gl.linkProgram(program);
        gl.useProgram(program);
      };

Then, I use the setup string to create a worker.

const worker = new Worker(URL.createObjectURL(new Blob([`(${setup})()`])));
console.log(worker);

There will throw a error in the worker becuase of _s().

I am not sure if this is a bug. If not, what could I do sometion to skip the generation of _s().

How often does this bug happen?

Every time

DevTools package (automated)

No response

DevTools version (automated)

No response

Error message (automated)

No response

Error call stack (automated)

No response

Error component stack (automated)

No response

GitHub query string (automated)

No response

ry-krystal commented 1 month ago

This issue arises because React's Hot Module Replacement (HMR) mechanism automatically inserts helper functions like _s(), which can cause problems when running in a Worker environment. Since the Worker environment is isolated, it cannot access the main thread's context and global variables, leading to errors. Solutions:

  1. Disable HMR Plugin: If you don't need hot updates inside the Worker, try disabling the HMR plugin for modules related to Worker. You can modify the webpack or rspack configuration to exclude the Worker files from HMR.Like this: module.exports = { plugins: [ new webpack.HotModuleReplacementPlugin({ exclude: /worker.js/ // Replace with your worker file path }), ], };
  2. Build Worker Separately: Separate the Worker logic into an independent file and build it separately. Then, use URL.createObjectURL to load the built file. This can be achieved using worker-loader for webpack/rspack or rollup plugins for vite.
  3. Use importScripts: If you need to share scripts within the Worker, consider using importScripts to load external resources.
  4. Avoid HMR Identifiers: Try using Babel or Webpack plugins to remove HMR-related identifiers. Specifically, configure babel to ignore HMR-inserted code.
Yukiniro commented 1 month ago

Thanks for your suggestion. And I noticed that it can use /* @refresh reset */ for some side effects, so, can we provide something like /* @refresh ignore*/ to avoid injecting code in some scenarios, such as WebGLRenderingContext.useProgram() ?

ry-krystal commented 1 month ago

Yes, introducing a / @refresh ignore / directive to tell the compiler or bundler to avoid injecting code in specific cases (like critical WebGL operations) is a practical and feasible solution. This would help you mark specific functions or sections of code that should not trigger automatic refresh behavior or have code injection. Example Usage: / @refresh ignore / const setupWebGL = () => { const program = gl.createProgram(); gl.linkProgram(program); gl.useProgram(program); // Avoid injecting HMR code here due to potential interference. };