Webbo3 Level 2 · JavaScript Development · Day 11 of 20
Storage and Application State
Making your application remember, and organising what it knows so that the screen can never disagree with the truth.
By the end of today you can
- Save and reload data with
localStorage, and know whensessionStorageis right - Turn objects into JSON for storage and read them back safely
- Keep one state object as the single source of truth
- Use one loop for every action: change state, save, render
- Restore saved data safely, even when it is missing, old or corrupt
- Decide where each kind of data should live
Every application you have built so far forgets everything the moment the page reloads. Your Day 5 tasks, your Day 9 filters: all gone.
Today they stop being gone. And on the way you meet the idea the whole of React is built on: one place that holds the truth, and a screen that is drawn from it.
1. Storage in the Browser
Definition
Web storage is a small space the browser gives your site to save text under names called keys. localStorage keeps it until it is deleted; sessionStorage keeps it only until the tab is closed.
Real-Life Example
localStorage is a notebook you keep in your bag: it is still there tomorrow. sessionStorage is a sticky note on a library desk: it survives while you are sitting there, and is thrown away when you leave.
Types
| localStorage | sessionStorage | |
|---|---|---|
| Survives a refresh | Yes | Yes |
| Survives closing the tab | Yes, indefinitely | No, it is gone |
| Shared between tabs | Yes, on the same site | No, one per tab |
| Rough size limit | About 5MB | About 5MB |
| Good for | Preferences, drafts, a small local database | A multi-step form, a one-visit filter |
The Four Methods
| Method | What it does |
|---|---|
localStorage.setItem('theme', 'dark') | Save a value under a key |
localStorage.getItem('theme') | Read it back, or null if it was never saved |
localStorage.removeItem('theme') | Delete one key |
localStorage.clear() | Delete everything this site saved |
Common Mistake: Storage Only Holds Text
Everything you store becomes a string. Save the number 42 and you read back "42". Save an object without converting it, and you read back the text "[object Object]", and the data is gone for good.
JSON.stringify on the way in, JSON.parse on the way out, always (Day 8).
Picture the notebook in your bag: it only takes writing. To keep a list, you write the list out (stringify), and to use it again you read it back (parse).
2. Reading Storage Safely
Definition
A safe read gets a stored value back without ever crashing the app. Three things can go wrong, and each needs handling: the key may not exist, the stored text may be corrupt, and in some browser modes storage throws an error the moment you touch it.
Real-Life Example
Opening your notebook: the page you want might be missing, the writing might be smudged, or the bag might be locked. A sensible person copes with all three and carries on, instead of giving up on the day.
Array.isArray(value) checks that the value really is a list, in case something else was saved under that key.
In Safari private browsing, and in any browser where the user has blocked site data, reading or writing localStorage throws an error. An unguarded getItem on the first line of your script takes the whole app down before it starts. Wrap it in try/catch and carry on without the saved data.
Picture the smudged notebook page: you do not throw the notebook away, you start that list fresh and carry on with your day.
3. One State Object
Definition
State is everything that is true about your app right now: the tasks, the filter, the search, whether it is loading. Keeping it in one object makes "what is true" a single thing you can print, save and restore.
Real-Life Example
A shop with one stock book, instead of notes scattered across the counter, the till and the manager's pocket. When the stock book says 3 bags of rice, that is the truth, and everyone reads the same book.
Do not keep a remainingCount in state beside the tasks. The moment a task is ticked you have two things to update, and one day you will update one and not the other. Calculate it when you render. A wrong number on screen is almost always a value that was stored when it should have been calculated.
Picture the one stock book. The number of empty shelves is never written in it: you count it from the book whenever someone asks.
4. The Loop: Change State, Save, Render
Definition
Every action goes through the same three steps, in the same order: change the state, save it, render the page from it. Nothing outside render ever writes to the page.
Real-Life Example
A bank teller: they update your account in the system, the system saves it, and then they print your new balance from the system. They never write a balance on the slip by hand.
How It Works
Object.assign(state, changes) copies the properties of changes into state, so setState({ filter: 'done' }) changes only the filter. [...state.tasks, newTask] builds a new list with one more task (Day 3 spread).
Both tasks, and the tick, came back after the reload, because render drew the page from state, and state was restored from storage.
Appending one row straight to the page instead of re-rendering means the array and the page are both "the truth". They agree today; in three weeks one code path updates the array and forgets the row, and the bug is hard to find because the data is right and the screen is wrong. Re-rendering the whole list is fast enough for hundreds of rows.
What If the Saved Data Is Broken?
Picture the bank teller: change the account, save it, print from it. Three steps, the same order, every time, and nothing is ever written on the slip by hand.
5. Restoring Saved Data That Is Out of Date
Definition
Saved data outlives your code. Defaults are the values your app starts with; spreading them underneath whatever was saved means a user who saved last week, before you added a new field, still gets that field.
Real-Life Example
A new form adds a "Phone number" box. Old paper forms in the filing cabinet do not have it. When you read an old form, you treat the missing box as blank, rather than rejecting the whole form.
The third line is the point: the saved data from an older version had no tasks field, and the defaults filled it in.
Picture the old paper forms in the cabinet: you read them with today's form in mind, and fill any missing box with the default.
6. Choosing Where to Keep Something
Definition
Different data needs to live for different lengths of time, and be seen by different people. Choosing the right place is part of designing the app.
Real-Life Example
A shopping list on your hand (gone after the trip), in your notebook (there next week), on the family fridge (everyone sees it), or at the bank (safe even if your house burns down).
| Keep it in | When | Example |
|---|---|---|
| A plain variable | Only needed while the page is open | Whether a dropdown is open |
| sessionStorage | Should survive a refresh but not closing the tab | Step 3 of a checkout |
| localStorage | Should be there next week | Theme, saved tasks, a draft |
| The URL | Should be shareable and bookmarkable | ?search=lagos&page=2 |
| A server | Must survive a new device, or matters if lost | Anything real |
localStorage can be read by any JavaScript running on your page, including anything injected through a security hole. Passwords, login tokens and personal data do not belong there. Day 17 explains why in detail.
Picture the list on your hand, in your notebook, on the fridge, or in the bank. The question is always: how long must it last, and who needs to see it?
Do This Now
- Refactor your Day 5 task app to one state object and the change, save, render loop.
- Save the tasks and the current filter. Refresh and confirm both come back.
- Open DevTools, Application tab, Local Storage, and watch your key change as you use the app.
- Break it on purpose: edit the stored value in DevTools to {{{ and reload. Your app must start with an empty list, not a blank white screen.
- Add a theme toggle saved in localStorage.
Add three tasks, tick one, set the filter to Active, close the browser completely, reopen it and go back to the page. All three tasks, the tick and the filter should be exactly as you left them. If the filter came back but the tick did not, you saved one and forgot the other.
Checkpoint
Q1. You saved an object without JSON.stringify. What do you read back?
The text "[object Object]". The data is gone and cannot be recovered. Stringify in, parse out, every time.
Q2. Why should a remaining-items count never be stored in state?
It can be calculated from the tasks. Storing it means two things to keep in step, and eventually one gets updated and the other does not.
Q3. Why wrap every localStorage call in try/catch?
It throws in private browsing, when the user has blocked site data, and when storage is full. An unguarded call on line one stops the whole app before it starts.
Q4. Why spread DEFAULTS before the saved data when restoring?
So data saved before you added a field still gets that field from the defaults. Saved data has old versions in the wild the moment you ship.
Tick Before You Move On
- ☐ My app has one state object and a change, save, render loop
- ☐ Tasks and filter survive closing the browser entirely
- ☐ A corrupted stored value starts the app empty rather than breaking it
- ☐ Nothing outside render writes to the page
- ☐ I calculate counts instead of storing them
- ☐ I watched my key update live in the Application tab
Quick Recap
Storage holds text only: stringify in, parse out. localStorage lasts; sessionStorage ends with the tab. Guard every storage call, because it really does throw in real browsers. One state object holds the truth; anything you can calculate, you calculate. Change state, save, render: in that order, for every action, with no exceptions. Restore with defaults underneath, and never store anything sensitive.
Final Scenario
A student uses your task app on the bus, closes the browser to save battery, and opens it again at home.
Everything is exactly where they left it, because every tick went through setState, was saved, and the page was drawn from state. Even the day their browser corrupted the saved data, the app simply started fresh instead of showing a white screen.
An app that remembers, and never lies about what it remembers: that is state done properly.
Tomorrow, Day 12: what the browser can already do. It knows the address bar, the clipboard, what is on screen and more, and using those beats rebuilding them.