Reputation: 401
I make a system jsonwebtoken in React and use Next.js. I find a problem when I run the code in the browser, that is, "localStorage is not defined". How can I fix it?
This is my code in file AuthStudentContext.js:
import React from 'react'
import axios from 'axios'
const axiosReq = axios.create()
const AuthStudentContext = React.createContext()
export class AuthStudentContextProvider extends React.Component {
constructor() {
super()
this.state = {
students: [],
student: localStorage.getItem('student') || {},
token: localStorage.getItem('token') || "",
isLoggedIn: (localStorage.getItem('student' == null)) ? false : true
}
}
login = (credentials) => {
return axiosReq.post("http://localhost:4000/api/login", credentials)
.then(response => {
const { token } = response.data
localStorage.setItem("token", token)
this.setState({
token,
isLoggedIn: true
})
return console.log(response)
})
}
And it shows error "localStorage is not defined".
Upvotes: 34
Views: 62722
Reputation: 9
this is working
export const Theme = { Light: "light", Dark: "dark" };
const [currentTheme, setCurrentTheme] = useState<string>();
// Local storage
useEffect(() => {
if (typeof window !== "undefined" && currentTheme) {
localStorage.setItem("theme", currentTheme);
}
}, [currentTheme]);
useEffect(() => {
setCurrentTheme(localStorage.getItem("theme") || Theme.Light);
}, []);
const siwtchTheme = () => {
currentTheme === Theme.Light
? setCurrentTheme(Theme.Dark)
: setCurrentTheme(Theme.Light);
};
Upvotes: 1
Reputation: 2305
localStorage
is only available client side. So Next.js will throw an error if you attempt to access localStorage to getItem
or setItem
.
Instead, inside the component, use a useEffect
hook to access the localStorage
on first client side render.
useEffect(() => {
localStorage.setItem("abc", "def");
}, []);
Upvotes: 1
Reputation: 2770
The window object and Localstorage won't be available when Next.js is building. So you need to check if the code is running in the browser. If you are running in React hooks you don't need to do this because hooks are always running browser side in React.
Just add these two utility functions to your Next.js project.
export const isBrowser = (): boolean => {
return typeof window !== 'undefined'
}
export const nextLocalStorage = (): Storage | void => {
if (isBrowser()) {
return window.localStorage
}
}
Then you can use it in your code like this:
nextLocalStorage()?.setItem('user', JSON.stringify(user))
Upvotes: 3
Reputation: 51
In addition to what SILENT said, this works for me:
React.useEffect(() => {
if (localStorage) {
const getLocalState = localStorage.getItem("headless");
console.log("LocalState: ", getLocalState)
}
}, []);
Upvotes: 5
Reputation: 4278
As everyone already mentioned, Next.js runs both on the client and server. On the server, there isn't any localStorage
, hence the undefined
error.
However, an alternative solution is to check if Next.js is running on the server before accessing the localStorage
. I.e.,
const ISSERVER = typeof window === "undefined";
if(!ISSERVER) {
// Access localStorage
...localStorage.get...
}
Upvotes: 32
Reputation: 42596
In the constructor
, as well as componentWillMount
lifecycle hooks, the server is still rendering the component. On the other hand, localStorage exists as part of the browser's window global, and thus you can only use it when the component is rendered. Therefore you can only access localStorage in the componentDidMount
lifecycle hook. Instead of calling localStorage in the constructor, you can define an empty state, and update the state in componentDidMount
when you can start to call localStorage.
constructor() {
super()
this.state = {
students: [],
student: undefined
token: undefined,
isLoggedIn: undefined
};
}
componentDidMount() {
this.login();
this.setState({
student: localStorage.getItem('student') || {},
token: localStorage.getItem('token') || "",
isLoggedIn: (localStorage.getItem('student' == null)) ? false : true
});
}
Upvotes: 15
Reputation: 14924
I never touched Next.js, but I guess its equivalent to Nuxt.js. So it does server-side rendering while you try to access localstorage on the client side.
You will need to use componentDidMount()
for this. Here is an example:
componentDidMount(){
localStorage.setItem('myCat', 'Tom');
alert("Tom is in the localStorage");
}
Otherwise, you could try with process.browser
:
if (process.browser) {
localStorage.setItem("token", token);
}
Upvotes: 12
Reputation: 148
I have created a function getLocalStorageItem and called this in useEffect with the required key name. After getting the value from localStorage, saved it in a state(i.e currentUser) and used it in initialState.
const [currentUser, setCurrentUser] = useState({});
const getLocalStorageItem = (key) => {
return typeof window !== undefined
? window.localStorage.getItem(key)
: null;
};
useEffect(() => {
setCurrentUser({
token: getLocalStorageItem("token"),
refreshToken: getLocalStorageItem("refreshToken"),
});
}, []);
const initialState = {
auth: {
isLoggedIn: true,
currentUser: currentUser,
},
};
Upvotes: 0