预期想在next.js中根据登录情况选择性在header中显示头像或登录按钮,但是往往并不能在组件中获取localstorage的值。若尝试这么做,将会报错:nextjs ReferenceError: localStorage is not defined
会发生这个问题,是因为next.js默认使用ssr。程序运行在服务器端,自然不存在window对象
为解决这一问题,我尝试使用如下方法解决:
- 通过context建立authprovider,使得登录情况可以在全局获取
- 通过hooks建立useauth,用usestate和usememo保存登录情况
- 通过swr以发送请求的方式请求localstorage的数据
显然,2方法会报错;1方法理论上可行,但实际操作时没有表现出很好的效果
最后使用了swr封装了一层请求localstorage的函数,代码如下:
import useSWR from 'swr';
// 需要判断代码是否运行在浏览器端
const isSSR = typeof window ==='undefined'
const useStorage = (key: string, defaultValue?: any) => {
if(isSSR) return;
const {data} = useSWR(key, async(key)=>{
const value = localStorage.getItem(key)||defaultValue;
return value;
});
const getToken = () => {
return data;
};
const setToken = (newVal: string) => useSWR([key, newVal], async([key, newVal])=>{
localStorage.setItem(key, newVal)
})
const delToken = () => useSWR(key, async(key)=>{
localStorage.removeItem(key)
});
return {
getToken,
setToken,
delToken
}
}
export default useStorage;
在组件中使用方式如下:
// src/components/header
import useStorage from "@/hooks/useStorage";
export default function Head() {
const authStorage = useStorage(tokenName);
if(!Boolean(authStorage?.getToken())){
Router.push('/login')
return <></>
}
return (
<section>
{
Boolean(authStorage?.getToken()) ?
<Avatar /> : <Link href="login">登录</Link>
}
</section>
)
}
需要注意的是,如果涉及到请求相关的函数(如自行创建请求示例来拦截每次请求,并尝试将token添加到auth头时),则不应该使用上文提到的authstorage,直接localstorage获取即可。
// src/service/request.ts
instance.interceptors.request.use(
(config: any) => {
const token = localStorage.getToken(token_name);
if (token) {
config.headers.Authorization = token;
}
return config
},
function (error) {
return Promise.reject(error)
}
)