【问题标题】:window is not defined when trying to access environment variables in Remix尝试访问 Remix 中的环境变量时未定义窗口
【发布时间】:2022-06-16 07:40:04
【问题描述】:

我正在尝试使用 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;

如果我注释掉console.log,我可以在&lt;body&gt; 中看到带有window.ENV = {...} 内容的文档顶部的&lt;script&gt;。但是取消注释 console.log 会向我显示错误消息并且没有 &lt;script&gt; 标记。这告诉我问题在于从文档中设置 window.ENV 而不是从我的组件中。

任何想法将不胜感激!

【问题讨论】:

    标签: remix.run


    【解决方案1】:

    您无法在该代码(组件渲染)中访问 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>
    

    但有时您需要立即在渲染方法中直接使用这些 env 值。您可以将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>
    }
    

    【讨论】:

    • 这与我一直在阅读和学习的有关 Remix 的内容相吻合。我现在对 Remix 的工作原理有了更好的了解,谢谢!
    • 我认为我们需要更好的方法来处理这个问题。它。目前处理这么简单的事情似乎非常痛苦。
    【解决方案2】:

    在 react 的组件级别范围内,如果不使用 useEffect 挂钩调用然后从那里引用窗口,就无法访​​问窗口对象可见的 dom。

    【讨论】:

      【解决方案3】:

      使用 Remix 文档建议的 window.ENV 模式的替代方法是在您的 Remix routes/root.tsx 文件中创建一个根 React 上下文,该文件将您的客户端环境变量包含在上下文的价值道具。然后根据需要通过相应的useRootContext 挂钩访问变量,而无需先使用useEffect。下面是一个使用 TypeScript 的简化示例:

      context/root-context.ts

      import { createContext, useContext } from 'react'
      
      export const RootContext = createContext({
          mapboxToken: '',
          stripePublicKey: '',
      })
      
      export const useRootContext = () => useContext(RootContext)
      

      routes/root.tsx

      import { useLoaderData, type LoaderFunction, json } from 'remix'
      import { RootContext } from '~/context'
      
      export const loader: LoaderFunction = async () => {
          return json({
              ENV: {
                  mapboxToken: process.env.MAPBOX_TOKEN,
                  stripePublicKey: process.env.STRIPE_PUBLIC_KEY,
              },
          })
      }
      
      const App = () => {
          const { ENV } = useLoaderData()
      
          return (
              <html lang="en">
                  <body>
                      <RootContext.Provider
                          value={{
                              mapboxToken: ENV.mapboxToken,
                              stripePublicKey: ENV.stripePublicKey,
                          }}
                      >
                          {/* app markup */}
                      </RootContext.Provider>
                  </body>
              </html>
          )
      }
      
      export default App
      
      

      通过useRootContext访问

      import { useRootContext } from '~/context'
      
      const BillingLayout = () => {
          const { stripePublicKey } = useRootContext()
      
          console.log(stripePublicKey)
       
          return (<Outlet />)
      }
      

      【讨论】:

        猜你喜欢
        • 2023-03-15
        • 2020-03-27
        • 1970-01-01
        • 1970-01-01
        • 2022-06-18
        • 2020-08-11
        • 1970-01-01
        • 2021-09-13
        • 2020-08-09
        相关资源
        最近更新 更多