How can I get values from local storage in next.js?When i give localStorage.getItem() in console,it is prnting the values.But when I assign this to a variable it is giving LocalStorage is not defined error.I have also added redux-persist in my localstorage
localStorage.getItem('id')
Local Storage is a Web API native to modern web browsers. It allows websites/apps to store data in the browser, making that data available in future browser sessions.
There are two React lifecycle methods we can use in our component to save/update the browsers localStorage when the state changes:
componentDidMount()
componentDidUpdate()
componentDidMount will run once your component has become available and loaded into the browser. This is when we gain access to localStorage. Since localStorage doesn’t reside in Node.js/Next.js since there is no window object, we will have to wait until the component has mounted before checking localStorage for any data. So If you want to assign the local storage value into a variable, please do this inside the componentDidMount method.
componentDidMount() {
const data = localStorage.getItem('id')
console.log(data);
if(data) {
//here you can set your state if it is necessary
}
}
And If we want to update our local storage value through the state we can easily update the localStorage value with our changes value by using componentDidUpdate. This method gets run each time the state changes so we can simply replace the data in localStorage with our new state.
componentDidUpdate() {
localStorage.setItem('id', JSON.stringify(this.state))
}
localStorage is a property of object window. It belongs to the browser, not next.js nor React, and accessing localStorage is not possible until React component has been mounted. So you need to ensure that your React app is mounted before calling localStorage, e.g. calling localStorage.getItem inside componentDidMount.
When working with a framework like Next.js that executes code on the server side, using localStorage produces an error like "localStorage is not defined" or "window is not defined"
To fix this, check to see if window is defined so that the code will run only when it's available.
This is a great article that explains more: https://blog.logrocket.com/using-localstorage-react-hooks/
See the section called, "Problems accessing localStorage for an SSR application"
You can create a file called "useLocalStorage.tsx" or whatever, and it would contain something like this:
import { useState, useEffect } from "react";
function getStorageValue(key, defaultValue) {
// getting stored value
if (typeof window !== 'undefined') {
const saved = localStorage.getItem(key);
return saved || defaultValue;
}
}
export const useLocalStorage = (key, defaultValue) => {
const [value, setValue] = useState(() => {
return getStorageValue(key, defaultValue);
});
useEffect(() => {
// storing input name
localStorage.setItem(key, value);
}, [key, value]);
return [value, setValue];
};
Then you can just import it into the file you want to use it in like this:
import { useLocalStorage } from './useLocalStorage'
Then you can call it to get the "id" from localStorage:
const [id, set_id] = useLocalStorage("id", "");
First think to take a note is, localStorage has nothing to do with next.js or redux-persist. localStorage is the internal window object and can be directly accessible without any definition.
I think you are trying to access the localStorage before it is being set, so you get that error.
Simple solution to this is to use Conditional (ternary) operator
,
const id = localStorage.getItem('id') ? localStorage.getItem('id') : "set your own default value";
console.log(id);
Related
I cannot get SvelteKit load function works when using it with Firebase, I always get this error message:
a load function related to route '/' returned a function, but must return a plain object at the top level (i.e. return {...})
I'm using onSnapshot here with Firestone to get the updated data whenever it changed on the database.
export function load() {
const queryParams = [orderBy('date')];
const q = query(collection(db, 'daily_status'), ...queryParams);
messagesUnsubscribeCallback = onSnapshot(
q,
querySnapshot => {
let data = querySnapshot.docs.map( doc => (
JSON.parse(JSON.stringify(
{
id: doc.id,
status: doc.data().status,
date: doc.data().date.toDate().toLocaleDateString('en-au'),
note: doc.data().note
}
))
))
return { daily_status: data }
})
return messagesUnsubscribeCallback;
}
It looks like your issue is the fact that you are returning the function onSnapshot() inside the load function. The only thing you can return inside a load method is a plain object as the error states. What you might want to do is to run the snapshot code inside an onMount.
Another solution would be creating a svelte store and pass the onSnapshot into the store. An example can be seen in this tutorial:
https://www.captaincodeman.com/lazy-loading-and-querying-firestore-with-sveltekit#introduction
Reference:
https://kit.svelte.dev/docs/load
Your load() function needs to run asynchronous code, so it can't return back the data directly. Instead, you need to change it to return a promise that resolves to the loaded data. For an example using fetch(), see:
https://kit.svelte.dev/docs/load#making-fetch-requests
In your case, you need to create your own promise.
Further more, the purpose of the load() function is to load the initial data the page needs to be able to render. As suggested by another, to subscribe to updates in the future, do that in the page component in onMount(), so you only subscribe to future updates when the component is rendered in the web browser.
I am currently working on a personal project in which I use both Vue 3 (with the composition api) and Firebase. I have noticed that Firebase makes frequent use of the observer pattern, but this has caused some confusion for me.
For example, the suggested way of getting the current authenticated user is using the onAuthStateChanged observer. However, say I implement this observer in a component and I now have a child component that wants to take the current user object as a prop, I have written something like this:
import { getAuth, onAuthStateChanged } from "firebase/auth";
import { ref } from "vue"
const curUser = ref()
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
curUser.value = user
} else {
curUser.value = null
}
});
Now this works (somewhat) but I wonder if this is the wrong way to approach this. The issue is that the value curUser is undefined until onAuthStateChanged is called. This means that if curUser is passed as a prop to a child component the child has to be able to deal with it being undefined.
Is this approach correct or does this qualify as code horror? I have encountered this issue many times, another example where this exact same issue plays is with the onValue observer
I'm trying to set the Custom time attribute in firebase on the front end. Everything is possible to set, like contentDisposition, custom Metadata etc, just can't find any way or any info about setting Custom time.
You can see it referenced here https://cloud.google.com/storage/docs/metadata#custom-time
You can set the custom time on the file manually in the Storage cloud console, but even when you do and you load the file in firebase on the front end, it's missing from the returned object! (makes me feel like it's not possible to achieve this)
var storage = this.$firebase.app().storage("gs://my-files");
var storage2 = storage.ref().child(this.file);
//// Tried this
var md = {
customTime: now.$firebase.firestore.FieldValue.serverTimestamp()
};
//// & Tried this
var md = {
Custom-Time: now.$firebase.firestore.FieldValue.serverTimestamp()
};
storage2.updateMetadata(md).then((metadata) => {
console.log(metadata);
}).catch((err) => {
console.log(err);
});
The reason I ask is I'm trying to push back the lifecycle delete date (which will be based on the custom time) every time the file is loaded. Does anyone know the answer or an alternative way of doing it?
Thanks in advance
The CustomTime metadata is not possible to update using Firebase JavaScript SDK since it is not included in the file metadata properties list mentioned in the documentation. So even if you specify it as customTime: or Custom-Time: the updateMetadata() method does not perform any changes.
I suggest you as a better practice, set the CustomTime metadata from the cloud console and modify the CustomTimeBefore Lifecycle condition from the back-end each time you load the file using the addLifeCycleRule method of the GCP Node.js Client.
// Imports the Google Cloud client library
const {Storage} = require('#google-cloud/storage');
// Creates a client
const storage = new Storage();
//Imports your Google Cloud Storage bucket
const myBucket = storage.bucket('my_bucket');
//-
// Delete object that has a customTime before 2021-05-25.
//-
myBucket.addLifecycleRule({
action: 'delete',
condition: {
customTimeBefore: new Date('2021-05-25')
}
}, function(err, apiResponse) {});
I have an app that loads some images with metadata. A single folder can be quite large (~100-142Mb) once loaded into memory. Previously, we were using a plain old javascript object to manage the state of the app and everything worked fine, but i'd like to gain the benefits of ngrx's state management.
I've discovered ngrx and it seemed to be a smarter option when it comes to state management. However, when i add these items to the state, the app hangs when adding images to the store and then performance slows down when accessing individual (and unrelated) flags from the store i.e. UI flag - draw is open.
1) Here "directories" is a Map < string, Directory > () object that is saved the the Store (~100-120Mb). Directory is a complex object with many nested values. Once images are loaded, and then added to the store, it a) hangs and then b) everything else (i.e. changing a ui flag) slows down.
return {
...state,
loadedDirectories: directories,
filesLoading: false,
};
2) The directories are then later accessed from the store.
this.store
.pipe(select(fromReducer.getLoadedDirectories))
.subscribe(loadedDirectories => {
this._directoryData = loadedDirectories;
});
Selector looks like this....
export interface ImageLoaderState {
loadedDirectories: Map<string, Directory>;
filesLoading: boolean;
errorMessage: string;
}
export class AppState {
imageLoader: fromImageLoader.ImageLoaderState;
}
export const combinedReducers = {
imageLoader: fromImageLoader.imageLoaderReducer
.... More reducers here ....
}
// Select Image loader state.
export const selectImageLoaderState = (state: AppState) => state.imageLoader;
export const getLoadedDirectories = createSelector(
selectImageLoaderState,
(state: fromImageLoader.ImageLoaderState) => state.loadedDirectories
);
Using angular 8 and the following versions of ngrx.
"#ngrx/effects": "^8.4.0",
"#ngrx/store": "^8.4.0",
"#ngrx/store-devtools": "^8.4.0",
Are there any better practices? i.e. Add each image, one at a time to the store?
The ngrx store is for application state and not so good as a document store.
Please see..
https://github.com/btroncone/ngrx-store-localstorage/issues/39
One issue I see is how you create your new state. You mention that when you create your new state, you do the following
return {
...state,
loadedDirectories: directories,
filesLoading: false,
};
I think you are creating an object with tons of key-value pairs, then recreating that work when you set the loadedDirectories property again. I'm uncertain about the performance costs of using the spread operator in the context of very large objects. I would suggest you focus on creating this property once. This might help you
Does spread operator affect performance?
I use a Cloud Function to generate a short unique URL on a record on the 'onWrite' event, and save it. This works well, but when I save a record from my Ember app using EmberFire, I do get a model back as an argument to a callback, but the URL of this model is undefined. Is there a way to return this back to the client? Or do I need to query the record to get the generated URL?
This is how my Cloud Function code looks:
exports.generateUrl = functions.database.ref('/tournaments/{tid}')
.onWrite(event => {
if (event.data.previous.exists()) {
return;
}
if (!event.data.exists()) {
return;
}
const url = shortid.generate();
return event.data.ref.update({ url });
});
Here is my component that saves data through form submission. I'm using an add-on called ember-changeset to handle some validations, but this shouldn't be related to the issue.
export default Ember.Component.extend({
submit(e) {
e.preventDefault();
let snapshot = this.changeset.snapshot();
return this.changeset
.cast(Object.keys(this.get('schema')))
.validate()
.then(() => {
if (this.changeset.get('isValid')) {
return this.changeset
.save()
.then((result) => {
// Here, result.get('url') is undefined.
})
}
})
}
});
If you have a function that writes new data back to a location in the database after a write, you'll have to keep listening to that location on the client in order to get that data back. Don't use a one-time read (once()), use a persistent listener (on()), and in that listener, make sure you're getting the URL or whatever you expect to be generated by the function. Then remove that listener if you don't need it any more.
(Sorry, I don't know Ember or what abstractions it provides around Realtime Database - I'm giving you the plain JavaScript API methods you'd use on a reference.)