React - 出现“localStorage未定义”错误

56

我正在尝试使用meta标签使我的网站对搜索引擎友好。 我在应用程序中实施服务器端渲染,但在此之后,我遇到了以下错误:

ReferenceError: localStorage未定义。

请帮忙解决这个问题。

我的package.json

{


 "main": "server.js",
 "scripts": {
  "start-dev": "NODE_ENV=development webpack -w & NODE_ENV=development node server.js",
  "test": "echo \"Error: no test specified\" && exit 1"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "dependencies": {
  "axios": "^0.18.0",
  "express": "^4.15.3",
  "firebase": "^4.12.1",
  "html2canvas": "^1.0.0-alpha.12",
  "react": "^16.2.0",
  "react-adsense": "0.0.5",
  "react-dom": "^16.2.0",
  "react-facebook-login": "^4.0.1",
  "react-google-login": "^3.2.1",
  "react-meta-tags": "^0.3.0",
  "react-router-dom": "^4.2.2",
  "react-router-match-as-promised": "^1.0.5",
  "react-scripts": "1.1.1",
  "react-share": "^2.1.1",
  "react-slick": "^0.22.3"
 },
 "devDependencies": {
  "autoprefixer": "^7.1.2",
  "babel-core": "^6.25.0",
  "babel-loader": "^7.1.1",
  "babel-preset-es2015": "^6.24.1",
  "babel-preset-react-app": "^3.1.2",
  "babel-preset-stage-0": "^6.24.1",
  "css-loader": "^0.28.4",
  "extract-text-webpack-plugin": "^2.1.2",
  "file-loader": "^0.11.2",
  "postcss-loader": "^2.0.6",
  "webpack": "^3.1.0",
  "webpack-node-externals": "^1.7.2"
 }
}

所有页面通常使用的元标签。 Home.js

<code>
import React, { Component } from 'react';
import axinst from '../common';
import {TabBox,TabBox2,TabBox3} from '../common/tabbox';
import Ads  from '../common/ads';
import SubscribeFrm from '../common/subscribefrm';
import MetaTags from 'react-meta-tags';
import AdSense from 'react-adsense';
import Loader from '../common/loader';

class Home extends Component {
  constructor(props) {
    super(props);
  }

  state = {
    header:[],
    otherSports:[],
    wizztalk:[],
    sports:[],
    isProgress:'',
    activeCat: '',
    metaTitle:'',
    metaDesc:'',
    metaKey:''
  }

  componentDidMount(){


    // axinst.get('/home')
    this.setState({isProgress:true});
    axinst.get('/home').then(response => {
      this.setState({isProgress:false});
      const header = response.data.data.Header;
      const sports = response.data.data.Sports;
      const otherSports = response.data.data.OtherSports;
      const wizztalk = response.data.data.Wizztalk;
      const metaTitle = response.data.data.metaTitle;
      const metaDesc = response.data.data.metaDesc;
      const metaKey = response.data.data.metaKeyword;
      this.setState({header});
      this.setState({sports});
      this.setState({otherSports})
      this.setState({wizztalk});
      this.setState({metaTitle});
      this.setState({metaDesc});
      this.setState({metaKey});
    }).catch(function (error) {
      // console.log(error);
      // console.log('error');
    });
  }

  render() {
    const hD = this.state.header;
    const sport = this.state.sports;
    return (
    <div id="maincontent">
        <MetaTags>
          <title>{this.state.metaTitle}</title>
          <meta name="title" content={this.state.metaTitle} />
          <meta name="keywords" content={this.state.metaKeyword} />
          <meta name="description" content={this.state.metaDesc} />
          <meta name="og:description" content={this.state.metaDesc} />
          <meta name="og:title" content={this.state.metaTitle} />
          <meta name="og:image" content={process.env.PUBLIC_URL +"/images/logo.png"}/>
        </MetaTags>
</code>

所以你想在服务器端使用 localStorage,对吗? - Alejandro
请检查我的package.json文件,并让我知道还需要看什么其他内容。 - Chander Shoor
是的,Alex,你是正确的。 - Chander Shoor
7个回答

87

当您在服务器上进行渲染时,您没有浏览器,因此您无法访问浏览器提供的所有API,包括localStorage

在同时在服务器和客户端(浏览器)上运行的JavaScript代码中,通常使用一个if子句来防范这种情况,该子句检查是否定义了window。“Window”是由浏览器提供的根对象,用于提供浏览器提供的所有API。

示例:

if (typeof window !== 'undefined') {
    console.log('we are running on the client')
} else {
    console.log('we are running on the server');
}

在您的情况下,您希望将对 localStorage 的调用放置在这样一个 if 条件语句中,例如:

if (typeof window !== 'undefined') {
    localStorage.setItem('myCat', 'Tom');
}

你的问题中的代码没有使用 localStorage。也许它在你导入的某个包中被使用,可能是 react-adsense。但是你没有包含你实际使用它的部分,所以我无法帮助你进一步。 - Patrick Hund
请问您能否指导我如何在我的应用程序中使用本地存储?如果您有任何有用的链接,请发送给我。 - Chander Shoor
1
我已经尝试过了:在你使用本地存储的任何地方,都要像我在代码示例中展示的那样,使用if语句检查它是否真正可用。 - Patrick Hund
我已经使用了你的代码,但仍然出现相同的错误。 - Chander Shoor
正如@PatrickHund所说,您可能需要检查依赖项的代码/文档以查找使用localStorage的软件包,并将其替换为_SSR友好_软件包。 - t3__rry

16

这个一行代码对我很有帮助:

const checkout = typeof window !== 'undefined' ? localStorage.getItem('checkout') : null

4

您可以为您的应用程序添加polyfill。例如,在您的index.js中的某个位置:

if (!window) {
    require('localstorage-polyfill');
}

使用这段代码后,我的整个代码都崩溃了。以下错误出现了。警告:无效的DOM属性“class”。您是指“className”吗? - Chander Shoor
1
不清楚怎么可以链接起来,但是是的,React使用className属性代替class - Alejandro
我已经使用了这个包来处理元标签 https://www.npmjs.com/package/react-meta-tags。我想让它更加友好地支持SEO。 - Chander Shoor
这是另一个问题,所以我假设你要求帮助的那个问题已经解决了。对于你提出的最后一个问题 - 只需将“class”替换为“className”,它应该有所帮助。然而,我要注意的是,将所有内容移动到服务器端渲染并不是一项简单的任务,您可能还会遇到其他问题。我建议您针对此问题提出新的具体问题(当然,如果之前没有被问过)。我认为没有人能在一个答案中修复您应用程序中的所有问题。 - Alejandro

2
在组件中,您可以使用useEffect钩子来等待完整的页面加载。
    useEffect(() => {
      localStorage.getItem('key');
    }, [input]);

对于钩子或函数,您可以使用if条件来检查窗口是否已加载,如下所示 -

    if(window !== 'undefined'){ 
      localStorage.getItem('key')
    }

这是正确的。useEffect只有在客户端组件渲染时才会加载,所以为了最佳实践,我们在useEffect中获取localStorage并使用useState来存储存储值。 - undefined

1

在我的情况下,我使用了useEffect来等待页面加载完成后再使用localStorage。


0

如果组件逻辑绝对依赖于lsVar的值来有条件地渲染某些内容,那么这是一种更可靠的方法。

    const Component = () => {
        if (typeof window === "undefined") return null;

        const lsVar = localStorage.getItem("lsVar");

        // component logic
        
        return (<></>);
    }

0
我在检查会话创建的查询参数值时遇到了NextJs的这个问题。
最好的解决方案是:
if (typeof window !== 'undefined') {
    console.log('Currently on Client side')
} else {
    console.log('Currently on Server Side');
}

在第一个检查(客户端)的内部,只需放置处理localStorage的所有代码。 这对我来说很好用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接