Skip to content
← 1001 Widgets
N° 038 · ●●○

To-do list that remembers

A to-do list saved in the browser, with filters and a counter of what is left. The right exercise to save an array of objects and read it back without breaking anything.

JS
What you'll learn
  • Save an array with JSON.stringify
  • Read data back without crashing if it is corrupted
  • Filter the display without touching the data
Why this language

Pourquoi une liste qui se souvient ? La liste de tâches de base oublie tout au rechargement. Ici, chaque changement est enregistré dans localStorage, et la liste revient telle quelle le lendemain.

localStorage ne garde que du texte : on transforme le tableau en JSON pour l'écrire, et on le relit avec JSON.parse. Si le contenu est abîmé ou absent, on repart d'une liste vide plutôt que de planter.

Les filtres ne modifient jamais les données : ils choisissent seulement ce qu'on affiche.

Pourquoi en JavaScript simple ? Une fonction changer qui modifie, sauvegarde et redessine : un seul chemin pour toutes les actions. Dans la démo, le cadre isolé bloque le stockage, la liste y vit en mémoire.

How it works
  1. LA CLÉ ET LA LECTURE

    Un JSON abîmé ou absent ne doit pas faire planter la page.

    const CLE = 'todo-memoire';
    function charger() {
      try {
        const v = JSON.parse(localStorage.getItem(CLE));
        return Array.isArray(v) ? v : [];
      } catch { return []; }
    }
    let taches = charger(), filtre = 'toutes';
  2. UN SEUL CHEMIN POUR CHANGER

    Modifier, sauvegarder, redessiner : toujours dans cet ordre.

    function changer(modifier) {
      modifier();
      try { localStorage.setItem(CLE, JSON.stringify(taches)); } catch { /* stockage bloqué */ }
      rendre();
    }
  3. LES ACTIONS

    const ajouter = texte => changer(() => taches.push({ id: Date.now(), texte, fait: false }));
    const cocher = (id, fait) => changer(() => { taches.find(t => t.id === id).fait = fait; });
    const supprimer = id => changer(() => { taches = taches.filter(t => t.id !== id); });
    const purger = () => changer(() => { taches = taches.filter(t => !t.fait); });
  4. FILTRER À L'AFFICHAGE

    Les données restent complètes, seul le rendu choisit.

    function visibles() {
      if (filtre === 'afaire') return taches.filter(t => !t.fait);
      if (filtre === 'faites') return taches.filter(t => t.fait);
      return taches;
    }
  5. D'UN ONGLET À L'AUTRE

    window.addEventListener('storage', e => {
      if (e.key === CLE) { taches = charger(); rendre(); }
    });
Pitfalls

PIÈGE 1 : Sauvegarder l'objet directement

Pitfall

Mauvais : localStorage.setItem('taches', taches). On enregistre le texte « [object Object] » et tout est perdu.

Fix

Correct : JSON.stringify à l'écriture, JSON.parse à la lecture.

PIÈGE 2 : Faire confiance à ce qu'on relit

Pitfall

Mauvais : JSON.parse sans try. Une ancienne version, une extension ou une modification à la main, et la page reste blanche.

Fix

Correct : un try et catch, puis vérifier que c'est bien un tableau avant de s'en servir.

PIÈGE 3 : Filtrer les données

Pitfall

Mauvais : taches = taches.filter(...) quand on clique sur « À faire ». En revenant à « Toutes », les tâches faites ont disparu.

Fix

Correct : filtrer une copie au moment de l'affichage, jamais l'état lui-même.

PERSONNALISER
  • Une date d'échéance par tâche, et les retards en tête de liste.
  • Le glisser-déposer pour réordonner, comme dans le tableau kanban.
  • La modification au double clic, avec Échap pour annuler.
OPTIMISATION
  • Ajoutez un numéro de version dans ce que vous enregistrez : le jour où le format change, vous saurez convertir les anciennes listes.
  • Pour des milliers d'éléments ou des fichiers, passez à IndexedDB, plus adapté que localStorage.
VERS REACT

Le tableau vit dans un useState initialisé avec une fonction : useState(charger), pour ne lire qu'une fois.

Un useEffect sur taches écrit dans localStorage à chaque changement.

Le filtre est un autre useState, et la liste visible se calcule au rendu.

Comments
0/1000

Loading comments…

Audience measurement

With your consent, this site measures its traffic with Google Analytics and the Meta pixel, via Google Tag Manager. Nothing is loaded until you choose, and you can change your mind at any time with the "Cookies" link at the bottom of the page.