necolas / react-native-web

Cross-platform React UI packages
https://necolas.github.io/react-native-web
MIT License
21.7k stars 1.79k forks source link

Vite in a SSR context #2668

Open zoriya opened 6 months ago

zoriya commented 6 months ago

Is there an existing issue for this?

Describe the issue

When using react-native-web with vite in SSR, it simply does not work. There seems to be due to issues with commonjs/modules handling.

After adding "inline-style-prefixer" to the list of commonJS packages to fix, rnw fails to load due to a module error on vendor/react-native/Utilities/clamp.js

Complete logs ```shell 1:41:16 AM [vite] Error when evaluating SSR module /node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js: |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) 1:41:16 AM [vite] Error when evaluating SSR module /node_modules/react-native-web/dist/vendor/react-native/VirtualizedList/index.js: failed to import "/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js" |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) 1:41:16 AM [vite] Error when evaluating SSR module /node_modules/react-native-web/dist/vendor/react-native/FlatList/index.js: failed to import "/node_modules/react-native-web/dist/vendor/react-native/VirtualizedList/index.js" |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) 1:41:16 AM [vite] Error when evaluating SSR module /node_modules/react-native-web/dist/exports/FlatList/index.js: failed to import "/node_modules/react-native-web/dist/vendor/react-native/FlatList/index.js" |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) 1:41:16 AM [vite] Error when evaluating SSR module /node_modules/react-native-web/dist/vendor/react-native/Animated/components/AnimatedFlatList.js: failed to import "/node_modules/react-native-web/dist/exports/FlatList/index.js" |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) 1:41:16 AM [vite] Error when evaluating SSR module /node_modules/react-native-web/dist/vendor/react-native/Animated/Animated.js: failed to import "/node_modules/react-native-web/dist/vendor/react-native/Animated/components/AnimatedFlatList.js" |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) 1:41:16 AM [vite] Error when evaluating SSR module /node_modules/react-native-web/dist/exports/Animated/index.js: failed to import "/node_modules/react-native-web/dist/vendor/react-native/Animated/Animated.js" |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) 1:41:16 AM [vite] Error when evaluating SSR module /node_modules/react-native-web/dist/index.js: failed to import "/node_modules/react-native-web/dist/exports/Animated/index.js" |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) 1:41:16 AM [vite] Error when evaluating SSR module /src/App.jsx: failed to import "/node_modules/react-native-web/dist/index.js" |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) 1:41:16 AM [vite] Error when evaluating SSR module /src/entry-server.jsx: failed to import "/src/App.jsx" |- ReferenceError: module is not defined at eval (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:13:1) at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) ReferenceError: module is not defined at /home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:11:1 at instantiateModule (file:///home/zoriya/projects/rnw-ssr-vite/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:15) ```

Expected behavior

SSR should work on vite

Steps to reproduce

Test case

https://github.com/zoriya/vite-rnw-ssr-repro

Additional comments

With @brillout, the creator of Vike (a SSR framework for vite), we tried to debug this the best we can, you can find our discussion and the vite issue below:

https://github.com/vikejs/vike/discussions/1637 https://github.com/vitejs/vite/issues/16679

zoriya commented 6 months ago

Turns out, import errors can be fixed by adding react-native-web to the list ssr.optimizeDeps.include like so:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import reactNative from "vite-plugin-react-native-web";
import { cjsInterop } from "vite-plugin-cjs-interop";

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [
        react(),
        reactNative(),
        cjsInterop({
            dependencies: ["inline-style-prefixer", "inline-style-prefixer/**"],
        }),
    ],
    ssr: {
        optimizeDeps: {
            include: ["react-native-web"],
        },
    },
});

Now the error is really wild while using a react-native-web componant in SSR:

Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
TypeError: Cannot read properties of null (reading 'useContext')
    at Object.useContext6 (/home/zoriya/projects/rnw-ssr-vite/node_modules/.vite/deps_ssr/react-native-web.js?v=7a5478b2:3119:29)
    at /home/zoriya/projects/rnw-ssr-vite/node_modules/.vite/deps_ssr/react-native-web.js?v=7a5478b2:29254:32
    at renderWithHooks (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5662:16)
    at renderForwardRef (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5846:18)
    at renderElement (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6009:11)
    at renderNodeDestructiveImpl (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6108:11)
    at renderNodeDestructive (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6080:14)
    at renderNode (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6263:12)
    at renderChildrenArray (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6215:7)
    at renderNodeDestructiveImpl (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6145:7)
    at renderNodeDestructive (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6080:14)
    at renderElement (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5975:9)
    at renderNodeDestructiveImpl (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6108:11)
    at renderNodeDestructive (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6080:14)
    at renderIndeterminateComponent (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5789:7)
    at renderElement (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5950:7)
    at renderNodeDestructiveImpl (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6108:11)
    at renderNodeDestructive (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6080:14)
    at renderElement (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5975:9)
    at renderNodeDestructiveImpl (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6108:11)
    at renderNodeDestructive (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6080:14)
    at retryTask (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6532:5)
    at performWork (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6580:7)
    at /home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6904:12
    at scheduleWork (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:78:3)
    at startWork (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6903:3)
    at renderToStringImpl (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6977:3)
    at Object.renderToString (/home/zoriya/projects/rnw-ssr-vite/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:7065:10)
    at render (/home/zoriya/projects/rnw-ssr-vite/src/entry-server.jsx:6:31)
    at file:///home/zoriya/projects/rnw-ssr-vite/server.js:54:28

(thanks @brillout for finding this)

brillout commented 6 months ago

Vike author here, this affects all Vite-based SSR frameworks. Not only Vike but also Remix, etc. Thus RNW cannot be used in all these frameworks.

necolas commented 6 months ago

The logs make it obvious where the first problem lies:

ReferenceError: module is not defined at /home/zoriya/projects/rnw-ssr-vite/node_modules/react-native-web/dist/vendor/react-native/Utilities/clamp.js:11:1

"module is not defined" at "react-native-web/dist/vendor/react-native/Utilities/clamp.js"

There's a stray module.exports instead of export in this file, which the bundler is probably having issues with in a Node env:

https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/vendor/react-native/Utilities/clamp.js#L23

Try fixing that and submitting a PR

zoriya commented 6 months ago

So I fixed that and the issue with hooks went away, I was not expecting that. I guess it's because using ssr.optimizeDeps.include also included dependencies like react.

I also looked at inline-style-prefixer, that you include via it's cjs entrypoint (/lib). I could not make it run via it's ejs entrypoint, which I think is due to an upstream bug. I'll try to fix this too soon.

jkhaui commented 3 months ago

@zoriya have you tried using npm aliases to install react-native-web instead of using the Vite plugin? This is how I normally set it up with Vite SSR-based frameworks and it seems to work.

Like so in your package.json:

"react-native": "npm:react-native-web"

zoriya commented 3 months ago

This prevents using react-native on platforms other than web.

For anyone else finding this issue: I stopped trying to make rnw run w/ vite & ssr. Most react native packages are cjs only (expo for example almost never ships esm). This leads to too many issues for me to consider vite a viable bundler for react-native. I am hopping we get a solution soon either via a new ssr framework based on bun, via react-native switching to esm or via vite working better w/ cjs.

jkhaui commented 3 months ago

This prevents using react-native on platforms other than web.

I can't help you much more because I only use rn on web (building an SSR framework with react-navigation as a universal router).

But if it's at least working on web now, could you do something like dynamically change the package source to use "actual" rn (instead of aliased rnw) depending on your environment?

I'm not sure how exactly, but dynamic imports + environment variables could be enough to achieve this. Or you could have a local copy of rnw symlinked, etc. Huge PITA for sure though

This leads to too many issues for me to consider vite a viable bundler for react-native. I am hopping we get a solution soon either via a new ssr framework based on bun, via react-native switching to esm or via vite working better w/ cjs.

I feel you. I tried to push some major rn packages to be more esm-friendly but got too busy to continue. Try checking out Nate's vxrn repo before throwing in the towel. We need more people working on Vite and cross-platform solutions.