【问题标题】:NodeInvocationException when trying to get data from localStorage in stateless React component尝试从无状态 React 组件中的 localStorage 获取数据时出现 NodeInvocationException
【发布时间】:2018-06-15 16:32:27
【问题描述】:

我已经这样实现了我的应用布局:

const Layout: React.SFC<IDefaultProps> = props => {
  const { component: Component, ...rest } = props;
  const logged = userServices.getUserLocalStorage();
  return (
    <Route
      {...rest}
      render={matchProps =>
        logged != null ? (
          <div className="container-fluid">
            <NavMenuLogged {...matchProps} />                
            <div className="row wu-content">
              <Component {...matchProps} />
            </div>                
            <Footer />
          </div>
        ) : (
          <Redirect to="/" />
        )
      }
    />
  );
};

希望将用户的登录信息保存在 localStorage 中,并从那里能够将用户重定向到登录;但我发现我无法访问 SFC 组件中的 localStorage。有人对此有解决方案吗? (优雅与否……)

这是 getUserLocalStorage() 函数:

export function getUserLocalStorage(){
    let user:Utils.User = JSON.parse(localStorage.getItem("user") || '{}');
    return user;
}

这是我得到的错误:

An unhandled exception occurred while processing the request.
NodeInvocationException: localStorage is not defined
ReferenceError: localStorage is not defined
at Object.getUserLocalStorage [as b] (E:\Trabajo\...\ClientApp\dist\main-server.js:9138:27)
at Layout (E:\...\ClientApp\dist\main-server.js:38836:102)
at ReactCompositeComponentWrapper._constructComponentWithoutOwner (E:\...\ClientApp\dist\vendor.js:43899:14)
at ReactCompositeComponentWrapper._constructComponent (E:\...\ClientApp\dist\vendor.js:43875:19)
at ReactCompositeComponentWrapper.mountComponent (E:\...\ClientApp\dist\vendor.js:43778:21)
at Object.mountComponent (E:\...\ClientApp\dist\vendor.js:11753:35)
at ReactCompositeComponentWrapper.performInitialMount (E:\...\ClientApp\dist\vendor.js:43961:34)
at ReactCompositeComponentWrapper.mountComponent (E:\Trabajo\...\ClientApp\dist\vendor.js:43848:21)
at Object.mountComponent (E:\...\ClientApp\dist\vendor.js:11753:35)
at ReactDOMComponent.mountChildren (E:\...\ClientApp\dist\vendor.js:47391:44)

【问题讨论】:

  • 你能发帖userServices.getUserLocalStorage();
  • 我已编辑问题以包含该功能
  • 你能把错误信息也贴出来吗?
  • 我添加了我得到的错误
  • 感觉错误发生在服务器上,而不是浏览器上。这是有道理的:Node 环境中没有localStorage。请确认您在服务器控制台而非浏览器中看到了错误。

标签: asp.net reactjs react-router typescript-typings server-side-rendering


【解决方案1】:

因为错误出现在服务端,在服务端渲染阶段,很有可能是Node环境下没有localStorage造成的。 Node 中根本没有这样的东西,它是特定于浏览器的 API。所以函数会变相抛出ReferenceError(可能是因为它是ASP.NET,但我不确定)。

我认为无论如何都无法在服务器端渲染步骤检索用户信息,并且这种特殊情况的用户数据仅存储在用户浏览器的本地存储中。在这种情况下,检查localStorage 是否存在可能是有意义的,如果不存在,回退到默认值:

export function getUserLocalStorage(){
    if (typeof localStorage === 'undefined') {
        // will safely return if localStorage is not defined
        return {};
    }

    let user:Utils.User = JSON.parse(localStorage.getItem("user") || '{}');

    return user;
}

typeof • 适用于任何,甚至是以前未定义的变量或引用。检查范围内是否存在某个引用是最安全的操作。

比较

if (localStorage) {
    // will throw ReferenceError is localStorage is not defined
}

因为此时您可能不想进行客户端/服务器代码拆分,所以typeof-powered 检查可能是最佳选择。这样,您可以确保以下代码仅在localStorage 引用可用的环境中运行(在大多数情况下,在浏览器的全局范围内)。您可能已经在应用相同的做法来检查是否定义了 window

希望对你有帮助!

【讨论】:

    【解决方案2】:

    这与您如何设置应用程序有关,当您调用localStorage 时,它必须发生在客户端,而不是服务器端。作为节点,没有本地存储。

    我会试试这个: 把它改成类组件而不是使用函数式组件,然后做

    componentDidMount(){
        console.log(localStorage) // or whatever you need to do here.
    } 
    

    如果您不想删除您的 SFC,您可以创建自己的 HOC,或使用类似 recompose 的库。这是使用 recompose 的代码 sn-p:

    const SFCWithLifeCycleHook = lifecycle({
      componentDidMount() {
        console.log(localStorage) //do something here...
      }
    })(SFC);
    

    【讨论】:

    • OP 应该如何在 SFC 上调用 componentDidMount
    • 可以改成类组件,是不是必须是函数式组件?
    • 如果代码库中有多个这样的组件怎么办?将 SFC 改造成类组件需要全部重写。
    • 你可以改为制作 HOC。保持功能不变。创建自己的 HOC,或使用 recompose 之类的库。我会更详细地更新帖子。
    • @Leogoesger,不,它不必是 SFC,事实上我正在考虑将其更改为类组件。这种变化会影响性能吗?
    猜你喜欢
    • 2020-09-01
    • 2018-02-17
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    • 2016-02-05
    • 2017-06-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多