JavaScript Design Patterns for Web Apps

Saving Todos in LocalStorage

Maximiliano Firtman

Maximiliano Firtman

Independent Consultant
JavaScript Design Patterns for Web Apps

Check out a free preview of the full JavaScript Design Patterns for Web Apps course

The "Saving Todos in LocalStorage" Lesson is part of the full, JavaScript Design Patterns for Web Apps course featured in this preview video. Here's what you'd learn in this lesson:

Max implements persistence by saving TodoList items with the localStorage API. An observer is added, so the data is saved any time an item is added or removed.

Preview
Close

Transcript from the "Saving Todos in LocalStorage" Lesson

[00:00:00]
>> Maximiliano Firtman: So far, okay, maybe you can trust me, okay? That we'll get better, but let's try to make it better. So let's add some ideas. For example, let's start by adding persistence. So now, every time I'm reloading, I don't need to create a new list. So let's add persistence.

[00:00:17]
And I'm trying to remember when I asked you this before, how to add persistent, and we had to dig into the DOM to actually get it, it was actually complicated, but let's try to do it with our current version. So I'm going to create a new file, storage.js, and storage.js will actually work with the list.

[00:00:39]
So let's first start by getting the singleton, okay? That's the list. I'm then going to export a locally storage object, a little object that I'm creating, then we shall have two functions, load and save, okay? We'll load and save, I need a comma here. There we are, we are using object literal, load and save.

[00:01:07]
Then it will use the local storage API, later in the future, you can change this for index EV or any other, or SQLite, to using WebAssembly, or any other API. If you wanna get more into that, you can check the data storage course at frontendmasters.io from anywhere. So we use different APIs there for storing data, but we will use just the basic one that will just take local storage, set item, so this will persist among different sessions.

[00:01:42]
We can call these todos, and you should stringify the collection. But we have a small problem, that we know how to solve anyway, sets cannot be converted into JSON directly with stringify. So we need to convert them into an array first, that we have already done before. Okay, so I'm going to create an array from that set.

[00:02:12]
Yeah, that set has some advantages, but also at the same time, it's adding some overhead on other places. So we are going to save this, and now for loading, by the way, when we should be executing save? So we wanna save this, when, which moments of my life cycle?

[00:02:37]
>> Speaker 2: Unified.
>> Maximiliano Firtman: Sorry
>> Speaker 2: Notify.
>> Maximiliano Firtman: Notify, well, going back to the, yeah, remember that from a consumer, we don't see the notify, okay? But when the data changes, and we already implemented the observer pattern there. So we can go here, directly here, and just talk to my TodoList and say, hey, can you add me another observer, and just call LocalStorage, I should say.

[00:03:02]
So I'm adding another observer that now we have two, the one that is rendering the list on the screen, and now we have added another one that is saving the data. So every time the data changes, it's not the best performance solution, okay, but we are not talking about performance today, we're talking about design patterns.

[00:03:23]
Well, to load, we need to do some stuff here first. Let's check, because the first time you load the web app, there is nothing in the LocalStorage, it's undefined. So let's check if we do have todos. If we do have todos, we need to do something like this.

[00:03:41]
So you may be tempted to go and say, or do the reverse operation, like JSON.parse of LocalStorage.getItem"todos" and store that in items. You're probably gonna be tempted for that, but there is a problem if we do that. So, I mean, I will change this code. So, we did create the replace list, okay, something like this, okay?

[00:04:15]
I mean, it seems okay, but there is a problem.
>> Maximiliano Firtman: This is an array, and the other one is a set, so we are kind of changing the nature. So JSON.parse returns an array. So instead of doing that, I mean, quickly, something that we can do, it can just convert that and then loop, like create.

[00:04:44]
>> Maximiliano Firtman: Let t of JSON.parse, like so, yep. And then, add one by one and create a new TodoItem with the text. It's like mapping back to my original structure, okay? So, saving happens automatically, but loading it might not happen automatically, okay? So we need to somehow find a way to call this.

[00:05:18]
Where do you think is a good spot to call LocalStorage load?
>> Speaker 3: ContentLoaded.
>> Maximiliano Firtman: Add DOMContentLoaded in App.js? Yeah, here. And by the way, we have a very large DOMContentLoaded, there is no need for that. So I can just, for example, close it here, and open another one.

[00:05:42]
In case you feel it feels better, so because this is the observer pattern as well in the DOM, so I can have several, one per topic. So we can separate and I can add another one here that will also be responsible for loading the data. So I will talk with LocalStorage.

[00:05:58]
Remember, this is coming from another module .load. And every time you're importing another module, please remember to go to the top and check that in this case it added the js. And don't ask me why, we don't have the .js now and no before, because I don't have the answer yet.

[00:06:16]
There should be an answer, there's always an answer, but I don't have it. Just always go and check. So to see if this works, let me refresh and let me add some here. So we have again, lunch, and now it's all working, so let's see why. Console, TodoList is not defined.

[00:06:37]
Okay, storage line one, because I didn't import TodoList, so it's correct. And now we don't have the .js, so let's add it. And now, TodoItem, okay, you're right. TodoItem as well, cool. Now we are back. By the way, where is this coming from? [LAUGH] So, it seems like my saving was actually working behind scenes.

[00:07:07]
So now I can add anything, if I refresh, it's still there.
>> Maximiliano Firtman: So now we have added one line in our app.js, just one line, this one, and now we have, automatically, data persistence. Yeah, we also created the class, but we are really the couple, so it was actually pretty easy to add this.

[00:07:33]
We didn't touch the DOM or anything so complicated, we just initialized something, and now it's working. So now we have persistence, so if I delete, refresh, it's still empty. Launch, let's say, finish the workshop. Okay, and then we'll get the gin tonic, wherever. And now if I refresh, I have persistence.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now