在盖茨比中使用document.cookie

时间:2020-07-15 00:36:41

标签: reactjs cookies gatsby

我需要能够在我的Gatsby项目中设置和访问Cookie,并且能够使用this tutorial进行可靠的设置。我正在建立一个设置cookie的钩子,并在整个站点中使用它。说完了,这就是助手的样子。

use-cookie.ts

import { useState, useEffect } from 'react';

const getItem = (key) =>
  document.cookie.split('; ').reduce((total, currentCookie) => {
    const item = currentCookie.split('=');
    const storedKey = item[0];
    const storedValue = item[1];

    return key === storedKey ? decodeURIComponent(storedValue) : total;
  }, '');

const setItem = (key, value, numberOfDays) => {
  const now = new Date();

  // set the time to be now + numberOfDays
  now.setTime(now.getTime() + numberOfDays * 60 * 60 * 24 * 1000);
  document.cookie = `${key}=${value}; expires=${now.toUTCString()}; path=/`;
};

/**
 *
 * @param {String} key The key to store our data to
 * @param {String} defaultValue The default value to return in case the cookie doesn't exist
 */

export const useCookie = (key, defaultValue) => {
  const getCookie = () => getItem(key) || defaultValue;
  const [cookie, setCookie] = useState(getCookie());

  const updateCookie = (value, numberOfDays) => {
    setCookie(value);
    setItem(key, value, numberOfDays);
  };

  return [cookie, updateCookie];
};

我正在像这样调用一个组件的钩子:

DealerList.tsx

import React, { ReactNode, useEffect } from 'react';

import { Container } from 'containers/container/Container';
import { Section } from 'containers/section/Section';
import { Link } from 'components/link/Link';

import s from './DealerList.scss';
import { useCookie } from 'hooks/use-cookie';

interface DealerListProps {
  fetchedData: ReactNode;
}

let cookie;
  
  useEffect(() => {
    cookie = useCookie();
  }, []);


export const DealerList = ({ fetchedData }: DealerListProps) => {
  const dealerInfo = fetchedData;
  if (!dealerInfo) return null;
  
  const [cookie, updateCookie] = useCookie('one-day-location', 'sacramento-ca');

  return (
    <>
      <Section>
        <Container>
          <div className={s.list}>
            {dealerInfo.map((dealer: any) => (
              <div className={s.dealer} key={dealer.id}>
                <div className={s.dealer__info}>
                  <h3 className={s.name}>
                    {dealer.company.name}
                  </h3>
                  <span className={s.address}>{dealer.address.street}</span>
                  <span className={s.city}>{dealer.address.city} {dealer.address.zip}</span>
                </div>
                <div className={s.dealer__contact}>
                  <span className={s.email}>{dealer.email}</span>
                  <span className={s.phone}>{dealer.phone}</span>
                </div>
                <div className={s.dealer__select}>
                  <Link
                    to="/"
                    className={s.button}
                    onClick={() => {
                      updateCookie(dealer.phone, 10);
                    }}
                  >
                    Select Location
                  </Link>
                </div>
              </div>
            ))}
          </div>
        </Container>
      </Section>
    </>
  );
};

它在gatsby develop上运行良好,我可以访问Cookie的值并更改相应显示的联系信息。但是,当我尝试构建或推送到Netlify时,出现此错误。

WebpackError: ReferenceError: document is not defined

我知道这与第4行和第17行的document.cookie有关,但是我正在努力寻找解决方法。有什么建议?我导入了useEffect,并且从我的研究中得知与此有关,但是该如何使其正常工作?

谢谢。

2 个答案:

答案 0 :(得分:2)

根据Gatsby's Debugging HTML Builds documentation

您的某些代码引用了“浏览器全局变量”,例如windowdocument。如果这是您的问题,则应该在上面看到类似的错误 “ window未定义”。要解决此问题,请找到有问题的代码,然后 要么a)在调用代码之前检查是否定义了window,以便 盖茨比(Gatsby)正在构建时代码未运行(请参见下面的代码示例),或者 b)如果代码在React.js组件的render函数中,请移动 该代码进入componentDidMount生命周期或useEffect挂钩, 这样可以确保除非在浏览器中,否则代码不会运行。

因此,在不违反钩子规则的情况下,在另一个钩子中调用一个钩子会导致嵌套无限循环。在调用之前,需要确保document的创建。只需添加一个检查条件:

import { useState } from 'react';

const getItem = (key) => {
  if (typeof document !== undefined) {
    document.cookie.split(`; `).reduce((total, currentCookie) => {
      const item = currentCookie.split(`=`);
      const storedKey = item[0];
      const storedValue = item[1];

      return key === storedKey ? decodeURIComponent(storedValue) : total;
    }, ``);
  }
};

const setItem = (key, value, numberOfDays) => {
  const now = new Date();

  // set the time to be now + numberOfDays
  now.setTime(now.getTime() + numberOfDays * 60 * 60 * 24 * 1000);
  if (typeof document !== undefined) {
    document.cookie = `${key}=${value}; expires=${now.toUTCString()}; path=/`;
  }
};

/**
 *
 * @param {String} key The key to store our data to
 * @param {String} defaultValue The default value to return in case the cookie doesn't exist
 */

export const useCookie = (key, defaultValue) => {
  const getCookie = () => getItem(key) || defaultValue;
  const [cookie, setCookie] = useState(getCookie());

  const updateCookie = (value, numberOfDays) => {
    setCookie(value);
    setItem(key, value, numberOfDays);
  };

  return [cookie, updateCookie];
};

由于您可能正在useCookie还不存在的组件或页面中调用document自定义钩子,因此我将使用相同的条件或使用{{1} }具有空的依赖项(useEffect,现在不会破坏钩子规则):

[]

答案 1 :(得分:2)

我做了一些进一步的研究,发现this simple hook,用此替换了use-cookie.ts中的代码,对其进行了一些修改(包括在下面),安装了universal-cookie并且它似乎工作正常。这是新代码:

use-cookie.ts

import { useState } from 'react';
import Cookies from 'universal-cookie';

export const useCookie = (key: string, value: string, options: any) => {
  const cookies = new Cookies();
  const [cookie, setCookie] = useState(() => {
    if (cookies.get(key)) {
      return cookies.get(key);
    }
    cookies.set(key, value, options);
  });

  const updateCookie = (value: string, options: any) => {
    setCookie(value);
    removeItem(value);
    cookies.set(key, value, options);
  };

  const removeItem = (key: any) => {
    cookies.remove(key);
  };

  return [cookie, updateCookie, removeItem];
};

但是,如果有人有更好的方法可以做到这一点,请告诉我!