简体   繁体   中英

Gradual Next.js adoption from within existing React application

I'm trying to find any examples I can that outline how to take an existing React application and gradually start migrating towards Next.js. I've read all the Next.js documentation I can regarding the incremental adoption strategies (eg subpaths, rewrites, microfrontends, proxies), but have yet to find a solid example showcasing what this actually looks like in a real-life "I've already got a massive React codebase" scenario.

The desired outcome is to corner off a subpath of my existing application, say /workshop , and have /workshop be the entrypoint to the Next.js application. I would then hope to be able to seamlessly navigate between the two applications, although the mechanics of that seem cloudy once you've entered the Next.js realm and need to navigate back out of the proxy.

For added context, both the React and Next.js applications will be served on Netlify. I've poured through their documentation, as well as Vercel's, looking for a working example but am still out of luck. Any knowledge or guidance on how best to approach this type of problem is greatly appreciated!

For those interested, I was able to create a working demo.

https://hybrid-next-react.vercel.app/

https://github.com/coloradocolby/hybrid-next-react

The way it works is fairly simple. It's a basic Next.js application with a catch all route of [...app].js which serves the entry point to the React SPA. A simple isMounted variable (could easily be refactored into a hook) ensures that the React SPA (which likely has browser-specific code) is never processed by the server.

import { useEffect, useState } from "react";
import CreateReactAppRoot from "../src/App";

const App = () => {
  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  // Don't render client-side app unless window is available.
  // NOTE: Page not statically pre-rendered nor server-side renderable
  return isMounted ? <CreateReactAppRoot /> : null;
};

export default App;

The final step is to make sure you implement full page reloads from the React SPA to any route intended for Next.js. This ensures the Next.js routing mechanism is fired. In the demo above, I created both Next.js and React specific navbars.

import React from "react";
import Link from "next/link";
import { useRouter } from "next/router";

export const NextNavbar = () => {
  const router = useRouter();
  return (
    <nav>
      {!router.pathname.includes("[...app]") && (
        <ul className="flex p-4">
          <li className="mr-3">
            <Link href="/" passHref>
              <div className="inline-block px-4 py-2 rounded cursor-pointer">
                Home
              </div>
            </Link>
          </li>
          ...
        </ul>
      )}
    </nav>
  );
};

Once again, although Next.js can intelligently link to the React SPA because of our catch all route, the React SPA requires a full page reload to any route intended for Next.js. As you might expect, if the destination route is meant to remain inside the React SPA, this is not necessary.

import React from "react";
import { Link } from "react-router-dom";

export const ReactNavbar = () => (
  <nav>
    <ul className="flex p-4">
      <li className="mr-3">
        <a href="/">
          <div className="inline-block px-4 py-2 rounded cursor-pointer">
            Home
          </div>
        </a>
      </li>
      ...
      <li className="mr-3">
        <Link to="/faq">
          <div className="inline-block px-4 py-2 rounded cursor-pointer">
            FAQ
          </div>
        </Link>
      </li>
      ...
    </ul>
  </nav>
);

One important caveat with this design is that it will not work if the CreateReactApp has global css imports, as Next.js requires all global styles to be imported in the _app.js file. From their documentation:

Due to the global nature of stylesheets, and to avoid conflicts, you may only import them inside pages/_app.js.

I imagine you could get around this by serving the Next.js and React SPA on different hosts and proxying between the two (NOTE: this would require custom external routing in the catch-all route), but it may be more trouble than it's worth. The easier solution may just be to refactor your global CSS into CSS modules .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM