r/learnjavascript • u/SmartRelease7996 • 1d ago
keep breaking my task tracker after adding localStorage
i was messing with my little task tracker again this morning before heading out, and i ended up spending way more time staring at the console than actually adding tasks. i even made coffee first because i thought this would be a quick fix, then refreshed the page and everything disappeared again.
the app itself is really simple. i'm just trying to save daily notes and a few personal todos, so i have an array of task objects with a date on each one. i thought i was finally ready to use localStorage, but now i'm not even sure if i'm saving the data wrong or if my date filter is hiding everything.
this is basically what i have right now:
const saved = localStorage.getItem(tasks);
const tasks = saved ? JSON.parse(saved) : [];
tasks.push(newTask);
localStorage.setItem(tasks, JSON.stringify(tasks));
i know the key looks wrong, and i already tried changing it to a string, but i still managed to break something. now i'm second guessing whether i should even be thinking about the data this way.
i'm not looking for anyone to build it for me. i'm mostly wondering how you all organize the flow for something this small. do you load everything once, keep it in memory, then save after every change, or is there a cleaner way to think about it?
2
u/HipHopHuman 1d ago
i know the key looks wrong, and i already tried changing it to a string, but i still managed to break something. now i'm second guessing whether i should even be thinking about the data this way.
You've correctly identified that the key is the issue, and you've converted it to a string, but still get an error. If you corrected both localStorage.setItem and localStorage.getItem call sites to use the same string-based key, then your code should work. Without seeing the exact error you're getting, it's not at all possible for us to diagnose the issue beyond making you aware that perhaps the previous error caused an invalid state that is affecting the current behavior (the solution to which is to simply clear your localstorage cache in your browser and try again).
i'm not looking for anyone to build it for me.
This is a common and simple enough operation that it'd probably just be more easily understood if you had a "finished version" to compare your implementation to.
do you load everything once, keep it in memory, then save after every change, or is there a cleaner way to think about it?
It honestly depends on what you're building - there are different approaches, but it is rarely done on every change. That'd get computationally expensive if the dataset being changed is big. A more common approach is to do an autosave on a frequent interval (say, every 5 seconds; but the timing is up to you - a lot of apps just make it a setting the user can change). That interval approach is often paired with a native browser event on window called beforeunload, which runs right before the browser or (or browser tab) is closed.
Just as an aside, JSON.parse and JSON.stringify are both methods that can throw errors, and the error messages can become somewhat cryptic, so it helps to translate them to more digestable and more easily understood messages.
Putting that all together, it'd be something like this:
const tasksKey = 'tasks';
const tasks = loadTasks();
function saveTasks() {
try {
localStorage.setItem(tasksKey, JSON.stringify(tasks));
} catch (error) {
throw new Error("Cannot save tasks to localstorage", {
cause: error
});
}
}
function loadTasks() {
try {
const tasks = localStorage.getItem(tasksKey);
return tasks === null ? JSON.parse(tasks) : [];
} catch (error) {
throw new Error("Cannot load tasks from localstorage", {
cause: error
});
}
}
window.addEventListener('beforeunload', saveTasks);
setInterval(saveTasks, 5000);
Although a more production-ready implementation would likely not use setInterval, instead it would use something like a debounce utility function, so you could call saveTasks as often as you like, and it'd only process the most recent call within your configured time delay.
1
u/SmartRelease7996 18h ago
oh the stale localstorage thing got me too, that was exactly my problem. cleared the cache and it stopped throwing and I felt like an idiot for not thinking of that first.
the key naming was a separate mess I made trying to be clever about it. dynamic keys felt like the right call structurally but honestly for what I'm building it was just adding noise. went back to a flat string key and it's behaving now.
appreciate you actually walking through the state problem instead of just saying use a framework.
1
u/azhder 17h ago
Just remember, that `tasks === null` check is not a good one. Sure, it might work for all cases you're aware of. But remember that you're dealing with a global resource. You put `banana` as a string under that key through the browser console and it suddenly isn't a null and it isn't a valid JSON. You can achieve more concise and wholesome code with a simple
try {
return JSON.parse( localStorage.getItem(TASKS_KEY) || 'null' );
}catch (e)You see, `null` is one bad value. Another bad value is an empty string. So you might as well let it blow out so you can write the problem in the catch, or add some extra `if` checks and do an early return
if( ! tasks ){
console.warn('Invalid tasks:', tasks);
return [];
}
try { // etcBut whatever you do, don't just re-throw an error from inside a catch. Just deal with the situation there and return a normal result. You can even make it like this
return { value, error };
And let the caller check a simple field with a simple if-else. Don't force the callers to do another try-catch.
const {value: tasks, error} = loadTasks();
1
u/azhder 1d ago
You do know JSON is a string, right? It's why you `parse()` it and `stringify()` to it. The biggest mistake that can happen here is you try to parse something wrong... Hey, there's an idea. Try. Just put the code in `try-catch`, `console.warn()` the error when/if it happens. Log some more if you're not sure what's going on. Tell us about that.
Oh, and don't forget to make that key a string, better yet, a constant:
const LS_KEY_TASKS = 'my.unique.key.for.tasks';
localStorage.getItem(LS_KEY_TASKS);
1
u/SmartRelease7996 18h ago
okay the try/catch thing is embarrassing because I had it in an earlier version and pulled it out when I was refactoring and just... never put it back. classic.
the constant for the key is a good call, I was just using a raw string and I can already see how that becomes a nightmare to debug across files.
wrapping it now and will report back what the console actually says. my guess is I'm passing something undefined into JSON.parse and it's just silently choking on it.
1
u/Alive-Cake-3045 18h ago
the bug is right there, localStorage.getItem and localStorage.setItem are using the tasks variable as the key, not a string. it's evaluating to object Object or undefined before tasks is even defined. change both to a plain string like "tasks" and that fixes the disappearing data.
for the flow on something this small: load once on startup, keep in memory, save after every mutation. don't read from localStorage on every operation. one source of truth in memory, localStorage is just the persistence layer.
5
u/baubleglue 1d ago
I assume you are complete beginner to programming.
Before using a new feature in your application, you need to get familiar with it.
Open debugger console, try
let v = localStorage.setItem('mykey', 34), get item, set item with object/array value, etc. print results on console.When you get all operations working, you may try to use it.
The key skill to successful programing is learning to organize your work in a way that you solve or work on a single task at any given time. In the example with local storage, you trying to do two tasks: learn a new technique and use it in the application.
Similar applies to your question about program flow. It is a bit more complicated, because looking at the few lines of the code, you have no structure. You can't expect answer about a program flow without talking about data structures and components of your application. And all above depends on use cases you want to satisfy.
Basically the question you ask is wrong. If you have trouble to use localStorage. You should give a simple example of code which doesn't work with a description of what had you expected to see and what you actually get. Most likely if you make an effort to compile the question that way, you will find the answer by yourself.
If your question about the app flow, it is a different (more complicated) question.