尝试访问 Remix 中的环境变量时未定义窗口

the*_*tuf 10 remix.run

我正在尝试使用 Remix 将一些环境变量输入浏览器,并且我一直在关注以下内容:

https://remix.run/docs/en/v1/guides/envvars

我已完全按照步骤 1 和 2 进行操作,但是我无法window.ENV从浏览器访问。我收到此错误:ReferenceError: window is not defined

这是我非常简单的组件:

function Test() {
  console.log('Window: ', window);
  return <div>Hello, Test</div>;
}

export default Test;
Run Code Online (Sandbox Code Playgroud)

如果我注释掉,我可以在文档顶部console.log看到和内容。但是取消注释会显示错误消息并且没有标签。这告诉我问题是来自文档的设置,而不是来自我的组件。<script><body>window.ENV = {...}console.log<script>window.ENV

任何想法将不胜感激!

Dan*_*ila 21

您无法访问window该代码中的对象(组件渲染),因为它既在服务器上运行(因为服务器端渲染)又在客户端上运行(就像常规客户端 React 应用程序一样)。服务器上没有window对象或任何其他浏览器 API。因此,您需要以这种方式编写代码,以便它可以在服务器和客户端上运行。

不过,您稍后仍然可以使用window对象,例如在useEffect某个onClick处理程序中,因为此代码只会在客户端运行:

// both this cases will work fine
  useEffect(() => {
    console.log(window.ENV);
  }, []);

// ...

      <button
        onClick={() => {
          console.log(window.ENV);
        }}
      >
        Log env
      </button>
Run Code Online (Sandbox Code Playgroud)

但有时您需要立即直接在渲染方法中使用这些环境值。您可以将loader函数与钩子结合使用useLoaderData,如下所示:

export function loader() {
  // process.env is available here because loader runs only on the server side
  return {
    SOME_SECRET: process.env.SOME_SECRET
  };
}

export default function Index() {
  const data = useLoaderData();

  // here you can use everything that you returned from the loader function
  console.log(data.SOME_SECRET);

  return <div>{/* ... */}</div>
}
Run Code Online (Sandbox Code Playgroud)


小智 9

使用 Remix 文档建议的模式的另一种方法是在 Remix paths/root.tsxwindow.ENV文件中创建根 React 上下文,其中在上下文的 value prop 中包含客户端环境变量。然后根据需要通过相应的钩子访问变量,而无需先使用。下面是一个使用 TypeScript 的简化示例:useRootContextuseEffect

上下文/根上下文.ts

import { createContext, useContext } from 'react'

export const RootContext = createContext({
    stripePublicKey: '',
})

export const useRootContext = () => useContext(RootContext)
Run Code Online (Sandbox Code Playgroud)

路线/root.tsx

import { useLoaderData, type LoaderFunction, json } from 'remix'
import { RootContext } from '~/context'

export const loader: LoaderFunction = async () => {
    return json({
        ENV: {
            stripePublicKey: process.env.STRIPE_PUBLIC_KEY,
        },
    })
}

const App = () => {
    const { ENV } = useLoaderData()

    return (
        <html lang="en">
            <body>
                <RootContext.Provider
                    value={{
                        stripePublicKey: ENV.stripePublicKey,
                    }}
                >
                    {/* app markup */}
                </RootContext.Provider>
            </body>
        </html>
    )
}

export default App

Run Code Online (Sandbox Code Playgroud)

访问通过useRootContext

import { useRootContext } from '~/context'

const BillingLayout = () => {
    const { stripePublicKey } = useRootContext()

    console.log(stripePublicKey)
 
    return (<Outlet />)
}
Run Code Online (Sandbox Code Playgroud)