【问题标题】:How to manage localStorage with nextJS?如何使用 nextJS 管理 localStorage?
【发布时间】:2023-02-20 17:06:55
【问题描述】:

通常我使用 Django 和 React 创建应用程序,使用 JWT 进行身份验证。 我正在测试 nextJS。 我无法管理 JWT 身份验证,因为我无法访问本地存储来获取我的 2 个令牌(访问+刷新)。

对于反应,我使用 axios.js:

import axios from 'axios';
import { baseURL } from './const/urls';

const axiosInstance = axios.create({
  baseURL: baseURL,
  timeout: 5000,
  headers: {
    Authorization: localStorage.getItem('access_token')
      ? 'JWT ' + localStorage.getItem('access_token')
      : null,
    'Content-Type': 'application/json',
    accept: 'application/json',
    'Cache-Control': 'no-cache',
    Pragma: 'no-cache',
    Expires: '0'
  }
});

axiosInstance.interceptors.response.use(
  (response) => response,
  (error) => {
    const originalRequest = error.config;

    // Prevent infinite loops
    if (
      error.response.status === 401 &&
      originalRequest.url === '/token/refresh/'
    ) {
      window.location.href = '/login/';
      return Promise.reject(error);
    }

    if (
      error.response.data.code === 'token_not_valid' ||
      error.response.status === 401 ||
      error.response.statusText === 'Unauthorized'
    ) {
      const refreshToken = localStorage.getItem('refresh_token');

      if (refreshToken) {
        const tokenParts = JSON.parse(atob(refreshToken.split('.')[1]));

        // exp date in token is expressed in seconds, while now() returns milliseconds:
        const now = Math.ceil(Date.now() / 1000);

        if (tokenParts.exp > now) {
          return axiosInstance
            .post('/token/refresh/', { refresh: refreshToken })
            .then((response) => {
              localStorage.setItem('access_token', response.data.access);
              localStorage.setItem('refresh_token', response.data.refresh);

              axiosInstance.defaults.headers['Authorization'] =
                'JWT ' + response.data.access;
              originalRequest.headers['Authorization'] =
                'JWT ' + response.data.access;

              return axiosInstance(originalRequest);
            })
            .catch((err) => {
              console.log(err);
            });
        } else {
          console.log('Refresh token is expired', tokenParts.exp, now);
          window.location.href = '/Login/';
        }
      } else {
        console.log('Refresh token not available.');
        window.location.href = '/Login/';
      }
    }

    // specific error handling done elsewhere
    return Promise.reject(error);
  }
);

export default axiosInstance;

但它不适用于 nextJS。 localStorage 或 window.localStorage 未定义。 我知道它不起作用,因为它试图在服务器端执行代码。 如何强制它只使用客户端代码?我看到我可以使用 Effect 但我不明白如何在我的案例中使用它。

【问题讨论】:

    标签: reactjs next.js


    【解决方案1】:

    使用 (typeof window === 'undefined') 条件来检查您是否可以访问您可以在客户端访问但不能在服务器端访问的窗口。我经常使用它,所以它有效。例如,你可以根据条件检查做这样的事情:

    // save to storage
    export const saveToStorage = (key, value) => {
        if(typeof window !== 'undefined') {
            return window.localStorage.setItem(key, value);
        }
    }
    
    // get from storage
    export const getFromStorage = (key) => {
        if (typeof window !== 'undefined') {
            return window.localStorage.getItem(key);
        }
    }

    【讨论】:

      【解决方案2】:

      NextJS 可以在窗口不可用的 nodeJS(服务器)上运行,你需要确保它只在客户端运行(可能是通过检查窗口是否未定义)但你需要知道你的请求赢了' 在服务器端工作。

      如果您需要 SSR,我建议将您的令牌保存在 cookie 上。

      if (typeof window !== "undefined") {
        // Client-side-only code
      }
      

      【讨论】:

        【解决方案3】:

        useEffect 钩子只在客户端运行。因此,访问其中的存储将保证定义了存储。这是一个自定义挂钩来完成这项工作:

        export default function useStorage(key, type = "sessionStorage") {
          const [value, setValue] = useState();
        
          // Initial fetch from storage
          useEffect(() => {
            const storage = type === "sessionStorage" ? sessionStorage : localStorage;
            setValue(storage.getItem(key));
          }, [key, type]);
        
          // Persist to storage
          useEffect(() => {
            // first render, don't override/destroy existing item value
            if (value !== undefined) {
              const storage = type === "sessionStorage" ? sessionStorage : localStorage;
              storage.setItem(key, value);
            }
          }, [key, value, type]);
        
          return [value, setValue];
        }
        

        用法:

        const [myValue, setMyValue] =  useStorage("my_value")
        

        【讨论】:

          猜你喜欢
          • 2020-12-09
          • 1970-01-01
          • 2021-03-16
          • 2020-09-06
          • 2021-11-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多