You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Next.js API与RecoilJS时出现无效URL错误的问题求助

问题解决:Next.js + Recoil 初始化Atom时出现 ERR_INVALID_URL 错误

问题根源

你遇到的TypeError [ERR_INVALID_URL]核心原因是**服务器端渲染(SSR)**时,Recoil Atom的默认值初始化逻辑在服务器端执行了:

  • 直接启动服务器或刷新页面时,Next.js会在服务器端渲染组件,此时getUsers()里的axios请求/api/users/getUsersFromDatabase会被当作完整URL解析,但服务器端没有浏览器的域名上下文,导致相对路径无效,触发错误。
  • 热重载时代码仅在客户端浏览器执行,相对路径会自动拼接当前域名,因此能正常工作。
    即使改用selector,若未处理服务器端执行的场景,同样会触发相同错误。

解决方案

方案1:用Recoil异步Selector + Loadable处理状态

Recoil的异步Selector需通过useRecoilValueLoadable/useRecoilStateLoadable处理加载、成功、失败状态,同时限制请求仅在客户端执行:

修改 atoms.js

import { atom, selector } from "recoil";
import { getUsers } from "../Modules/getUsers";

// 异步Selector:仅在客户端发起请求
export const userListSelector = selector({
  key: "userListSelector",
  get: async () => {
    if (typeof window !== "undefined") {
      const data = await getUsers();
      return data;
    }
    return []; // 服务器端返回默认空数组
  },
});

// 以Selector作为Atom默认值
export const userListAtom = atom({
  key: "userListAtom",
  default: userListSelector,
});

修改 UserDisplay.tsx

import React from "react";
import { useRecoilValueLoadable } from "recoil";
import { userListAtom } from "../Atoms/atoms";

const UserDisplay = () => {
  const userListLoadable = useRecoilValueLoadable(userListAtom);

  switch (userListLoadable.state) {
    case "loading":
      return <div>加载中...</div>;
    case "hasError":
      return <div>获取用户失败:{userListLoadable.error.message}</div>;
    case "hasValue":
      console.log(userListLoadable.contents);
      return <div>用户列表已加载</div>;
  }
};

export default UserDisplay;

方案2:将组件改为客户端仅渲染

利用Next.js动态导入,强制组件只在客户端执行,避免服务器端触发请求:

在页面组件中修改 UserDisplay 的导入

import dynamic from "next/dynamic";

// 禁用SSR,组件仅在客户端渲染
const UserDisplay = dynamic(() => import("../components/UserDisplay"), {
  ssr: false,
  loading: () => <div>加载中...</div>,
});

export default function Home() {
  return <UserDisplay />;
}

这样getUsers()只会在客户端执行,相对路径能正常解析为完整URL。


方案3:服务器端获取数据后传递给客户端

在页面组件中通过getServerSideProps直接请求Firebase Cloud Function(服务器端可用完整URL),再将数据传递给组件同步到Recoil Atom:

页面组件(例:pages/index.tsx)

import React, { useEffect } from "react";
import { useSetRecoilState } from "recoil";
import { userListAtom } from "../Atoms/atoms";
import UserDisplay from "../components/UserDisplay";
import axios from "axios";

export default function Home({ initialUsers }) {
  const setUserList = useSetRecoilState(userListAtom);

  useEffect(() => {
    // 将服务器端获取的数据同步到Recoil
    setUserList(initialUsers);
  }, [setUserList, initialUsers]);

  return <UserDisplay />;
}

export async function getServerSideProps() {
  try {
    // 服务器端直接请求Firebase Cloud Function,使用完整URL
    const response = await axios.get("你的Cloud Function完整URL");
    return { props: { initialUsers: response.data } };
  } catch (error) {
    return { props: { initialUsers: [] } };
  }
}

修改 atoms.js

import { atom } from "recoil";

export const userListAtom = atom({
  key: "userListAtom",
  default: [], // 初始空数组
});

这种方式可避免客户端额外请求API,同时彻底解决服务器端URL无效的问题。


内容的提问来源于stack exchange,提问作者joel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 07:45:59