iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
😺

Running Remix3 with Vite and HMR Support

に公開

Previous Article

https://zenn.dev/sora_kumo/articles/remix3-samples

This Repository

https://github.com/SoraKumo001/remix3-sample02

Using Remix3 from Vite

In the previous article, we built with rolldown and executed by calling scripts from an HTML file. This time, we will try running it from Vite with HMR support.

Vite Configuration

vite.config.ts

We will create a plugin that reloads the top-level script file when a file change is detected. Also, just like last time, we will set up aliases for JSX.

import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    {
      name: "reload",
      handleHotUpdate({ server }) {
        server.moduleGraph.getModuleByUrl("/src/main.tsx").then((mod) => {
          if (mod) server.reloadModule(mod);
        });
      },
    },
  ],
  base: "./",
  resolve: {
    alias: {
      "react/jsx-runtime": "@remix-run/dom/jsx-runtime",
      "react/jsx-dev-runtime": "@remix-run/dom/jsx-dev-runtime",
    },
  },
});

Sample Program

main.tsx

This is the entry point file. We include a workaround to prevent a full reload during updates.

import { App } from "./App";
import { createRoot } from "@remix-run/dom";

createRoot(document.getElementById("root")!).render(<App />);

if (import.meta.hot) {
  import.meta.hot.accept(() => {});
}

App.tsx

This is the count button component used in the previous article.

import { type Remix } from "@remix-run/dom";
import { dom } from "@remix-run/events";
import { Test } from "./Test";

export function App(this: Remix.Handle) {
  let count = 0;
  return () => (
    <>
      <button
        on={[
          dom.click(() => {
            count++;
            this.render();
          }),
        ]}
      >
        Count: {count}
      </button>
      <Test value="test" />
    </>
  );
}

Test.tsx

This is for verifying mouseover events.

import { type Remix } from "@remix-run/dom";
import { dom } from "@remix-run/events";

export function Test(this: Remix.Handle) {
  let mouseState = "mouseOut";
  return ({ value }: { value: string }) => (
    <div
      on={[
        dom.mouseover(() => {
          mouseState = "mouseOver";
          this.render();
        }),
        dom.mouseout(() => {
          mouseState = "mouseOut";
          this.render();
        }),
      ]}
    >
      {value}:{mouseState}
    </div>
  );
}

HMR and States That Are Not Preserved

I succeeded in preventing a full reload during HMR, keeping the HTML file as is while making only the script files targets for reloading. However, there is a fundamental problem.

The data area corresponding to React's state in each component is a variable within the function. However, if the JSX structure is modified, it's necessary to return the new content as a return value, which requires re-executing the function. When this happens, the contents of the variables are naturally initialized. In other words, there is no way to maintain state during HMR like React does.

As for where React stores its state, it places them in a specific order within external variables managed by React. Remix3, which does not have such a structure, is currently unable to implement functionality to maintain state.

Conclusion

Remix3 is still under development, so there is a high possibility that the specifications will be significantly modified in the future. Since there are many issues to be resolved, I have a feeling that it will take quite a while before it is officially released.

GitHubで編集を提案

Discussion