Eigene Goal-Streak-Logik mit React Hooks und LocalStorage
Lernen Sie, einen wiederverwendbaren Goal-Streak-Hook in React zu erstellen: localStorage für die Speicherung und Datumslogik für tägliche Gewohnheiten.
Auf dieser Seite
Gamification ist ein leistungsstarkes Werkzeug in der Webentwicklung, um die Nutzerbindung zu steigern. Eine der wirksamsten Funktionen ist die „Streak“, die Nutzerinnen und Nutzer dazu anregt, täglich zurückzukehren, um ihren Fortschritt aufrechtzuerhalten. Denken Sie an Duolingos tägliche Lektionsstreak oder an GitHubs Beitragsgrafik. In diesem Tutorial erstellen wir einen eigenen React-Hook, useGoalStreak, um eine Streak für die tägliche Zielerreichung zu verfolgen.
Wir bauen eine einfache Anwendung, die ein tägliches Schrittziel verfolgt. Der Kern unserer Logik ist ein eigener Hook, der den localStorage des Browsers verwendet, um die Streak-Daten dauerhaft zu speichern. So bleibt die Streak erhalten, wenn jemand die Anwendung erneut besucht. Außerdem behandeln wir die Logik, mit der die Streak zurückgesetzt wird, wenn ein Tag ausgelassen wird.
Voraussetzungen
- Grundlegendes Verständnis von React und React Hooks (
useState,useEffect). - Node.js und npm (oder yarn) sind auf Ihrem Rechner installiert.
- Ein Code-Editor wie VS Code.
Das Problem verstehen
Eine tägliche Streak zu verfolgen wirkt auf den ersten Blick einfach, doch es gibt einige knifflige Sonderfälle:
- Persistenz: Wie merken wir uns die Streak, wenn der Browser-Tab geschlossen wird?
- Datumswechsel: Wie erkennen wir, ob die letzte Erfüllung gestern oder vor einer Woche war?
- Zeitzonen: Wie stellen wir sicher, dass ein „Tag“ für die nutzende Person konsistent ist?
- Wiederverwendbarkeit: Wie machen wir diese Logik für verschiedene Ziele leicht wiederverwendbar?
Unsere Lösung ist ein eigener Hook, der diese gesamte Komplexität kapselt und jeder Komponente, die ihn benötigt, eine klare und unkomplizierte API bereitstellt. Für die Persistenz verwenden wir localStorage, eine einfache, browserbasierte Speicherlösung, die sich für diese Art nicht sensibler Daten gut eignet.
Voraussetzungen
Richten wir ein neues React-Projekt ein, um zu beginnen.
npx create-react-app goal-streak-tutorial
cd goal-streak-tutorial
Für dieses Tutorial sind keine externen Bibliotheken erforderlich; wir verwenden die Standard-React-Hooks und Browser-APIs.
Schritt 1: Den eigenen Hook useGoalStreak erstellen
Erstellen Sie zuerst eine neue Datei src/useGoalStreak.js. Dort befindet sich unsere gesamte Logik.
Was wir tun
Wir definieren die Struktur unseres eigenen Hooks. Er muss:
- seinen Zustand aus dem
localStorageinitialisieren. - eine Möglichkeit bieten, die aktuelle Streak-Zahl abzurufen.
- eine Funktion bereitstellen, mit der das Tagesziel als erfüllt markiert wird.
Umsetzung
Hier ist die Grundstruktur unseres Hooks:
// src/useGoalStreak.js
import { useState, useEffect }
from 'react';
export const useGoalStreak = (storageKey) => {
const [streak, setStreak] = useState(0);
// More logic will go here
return {
streak
};
};```
### So funktioniert es
Wir beginnen mit einem einfachen eigenen Hook, der einen `storageKey` als Argument entgegennimmt. Dadurch können wir den Hook für verschiedene Ziele wiederverwenden, indem wir deren Streaks unter unterschiedlichen Schlüsseln im `localStorage` speichern. Vorerst gibt er lediglich eine fest codierte Streak von 0 zurück.
## Schritt 2: Aus dem LocalStorage lesen
Als Nächstes lassen wir unseren Hook beim ersten Rendern aus dem `localStorage` lesen.
### Was wir tun
Wir verwenden die verzögerte Initialisierung von `useState`, um den Anfangszustand aus dem `localStorage` zu holen. Das ist effizienter, weil es nur einmal beim ersten Rendern ausgeführt wird. Außerdem müssen wir den Fall behandeln, dass im `localStorage` noch keine Daten vorhanden sind.
### Umsetzung
```javascript
// src/useGoalStreak.js
import {
useState
} from 'react';
// A helper function to check if two dates are the same day
const isSameDay = (date1, date2) =>
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
export const useGoalStreak = (storageKey) => {
const [streak, setStreak] = useState(() => {
try {
const storedData = window.localStorage.getItem(storageKey);
if (storedData) {
const data = JSON.parse(storedData);
const today = new Date();
const lastCompletion = new Date(data.lastCompletion);
// If the last completion was today, we're still on the same streak
if (isSameDay(today, lastCompletion)) {
return data.streak;
}
// Check if the last completion was yesterday to continue the streak
const yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
if (isSameDay(yesterday, lastCompletion)) {
return data.streak;
}
}
} catch (error) {
console.error("Error reading from localStorage", error);
}
// Reset streak if more than a day has passed
return 0;
});
// ...
return {
streak
};
};
So funktioniert es
- Wir verwenden eine Funktion innerhalb von
useState, um unseren Zustand verzögert zu initialisieren. - Wir lesen mit dem übergebenen
storageKeyaus demlocalStorage. - Wenn Daten vorhanden sind, parsen wir sie und vergleichen das Datum von
lastCompletionmit dem heutigen und dem gestrigen Datum. - War die letzte Erfüllung gestern, behalten wir die Streak. Lag sie davor, ist die Streak unterbrochen, und wir geben
0zurück. - Die Hilfsfunktion
isSameDayist für genaue Datumsvergleiche entscheidend, da sie den Zeitanteil desDate-Objekts ignoriert.
Schritt 3: In den LocalStorage schreiben und die Streak aktualisieren
Als Nächstes benötigen wir eine Funktion, mit der das Tagesziel für den Tag als erfüllt markiert werden kann.
Was wir tun
Wir erstellen eine Funktion namens markGoalAsCompleted, die von unserem Hook zurückgegeben wird. Diese Funktion aktualisiert die Streak-Zahl und speichert den neuen Zustand im localStorage.
Umsetzung
// src/useGoalStreak.js
import {
useState
} from 'react';
const isSameDay = (date1, date2) =>
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
export const useGoalStreak = (storageKey) => {
const [streakData, setStreakData] = useState(() => {
try {
const storedData = window.localStorage.getItem(storageKey);
if (storedData) {
const data = JSON.parse(storedData);
const today = new Date();
const lastCompletion = new Date(data.lastCompletion);
// If last completion was yesterday, the streak continues
const yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
if (isSameDay(yesterday, lastCompletion)) {
return { ...data, streak: data.streak };
}
// If last completion was today, don't change anything
if (isSameDay(today, lastCompletion)) {
return data;
}
}
} catch (error) {
console.error(error);
}
// Default state
return {
streak: 0,
lastCompletion: null
};
});
const markGoalAsCompleted = () => {
const today = new Date();
const lastCompletion = streakData.lastCompletion ? new Date(streakData.lastCompletion) : null;
// If goal was already completed today, do nothing
if (lastCompletion && isSameDay(today, lastCompletion)) {
return;
}
const yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
const newStreak = (lastCompletion && isSameDay(yesterday, lastCompletion)) ?
streakData.streak + 1 :
1;
const newData = {
streak: newStreak,
lastCompletion: today.toISOString(),
};
setStreakData(newData);
window.localStorage.setItem(storageKey, JSON.stringify(newData));
};
return {
streak: streakData.streak,
markGoalAsCompleted
};
};
So funktioniert es
- Die Funktion
markGoalAsCompletedprüft zuerst, ob das Ziel heute bereits erfüllt wurde. - Anschließend ermittelt sie, ob
lastCompletiongestern war. Ist das der Fall, erhöht sie die Streak. Andernfalls beginnt sie eine neue Streak mit dem Wert 1. - Es wird ein neues Datenobjekt mit der aktualisierten Streak und dem aktuellen Datum als
lastCompletionerstellt. - Diese neuen Daten werden sowohl in unserem React-Zustand (mit
setStreakData) als auch imlocalStoragegespeichert.
Alles zusammenfügen
Verwenden wir nun unseren neuen Hook useGoalStreak in einer Komponente.
Umsetzung
Aktualisieren Sie src/App.js mit folgendem Code:
// src/App.js
import React from 'react';
import {
useGoalStreak
} from './useGoalStreak';
import './App.css';
function App() {
const {
streak,
markGoalAsCompleted
} = useGoalStreak('stepGoal');
return ( <
div className = "App" >
<
header className = "App-header" >
<
h1 > Daily Step Goal Tracker < /h1> <
div className = "streak-display" >
Your current streak is: <
span className = "streak-count" > {
streak
} < /span> day(s) 🔥 <
/div> <
button onClick = {
markGoalAsCompleted
} >
I met my 10, 000 step goal today!
<
/button> <
/header> <
/div>
);
}
export default App;
Für etwas grundlegendes Styling aktualisieren Sie src/App.css:
/* src/App.css */
.App {
text-align: center;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.streak-display {
margin: 20px 0;
}
.streak-count {
font-size: 1.5em;
font-weight: bold;
color: #61dafb;
}
button {
font-size: calc(8px + 2vmin);
padding: 10px 20px;
border-radius: 8px;
border: none;
background-color: #61dafb;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #4fa8c5;
}
Starten Sie nun Ihre Anwendung:
npm start
Sie sollten Ihren Ziel-Tracker sehen. Klicken Sie auf die Schaltfläche, um Ihre Streak zu starten. Aktualisieren Sie die Seite oder schließen Sie den Tab und öffnen Sie ihn erneut – Ihre Streak ist weiterhin vorhanden. Warten Sie bis morgen, dann können Sie Ihre Streak fortsetzen. Wenn Sie einen Tag auslassen, wird sie auf 0 zurückgesetzt.
Bewährte Sicherheitspraktiken
Für diese Anwendung ist localStorage im Allgemeinen sicher. Denken Sie jedoch daran, dass localStorage kein sicherer Speicher ist. Er ist über JavaScript derselben Domain zugänglich und daher anfällig für Cross-Site-Scripting-Angriffe (XSS). Speichern Sie niemals sensible Informationen wie Benutzer-Tokens oder personenbezogene Daten im localStorage. Für unsere Ziel-Streak ist er vollkommen geeignet.
Alternative Ansätze
- Session Storage: Wenn die Streak nur für die aktuelle Browsersitzung erhalten bleiben soll, können Sie statt
localStoragesessionStorageverwenden. Die API ist identisch. - Backend-Datenbank: Für eine robustere Lösung, die geräteübergreifend synchronisiert wird, würden Sie die Streak-Informationen in einer Backend-Datenbank speichern, die einem Benutzerkonto zugeordnet ist. Das ist komplexer, für produktive Anwendungen mit Benutzerauthentifizierung jedoch erforderlich.
- Bibliotheken von Drittanbietern: Bibliotheken wie
date-fnskönnen Datumsvergleiche robuster und lesbarer machen, insbesondere beim Umgang mit Zeitzonen.
Fazit
Wir haben erfolgreich einen wiederverwendbaren eigenen React-Hook, useGoalStreak, erstellt, der die Logik zur Verfolgung einer täglichen Ziel-Streak kapselt. Wir haben gesehen, wie localStorage für die Datenpersistenz genutzt wird und wie datumsbasierte Logik die Streak korrekt berechnet.
Dieses Muster ist äußerst vielseitig. Sie können es verwenden, um jede tägliche Gewohnheit zu verfolgen, vom Schreiben eines Tagebucheintrags bis zum Absolvieren eines Trainings.
Nächste Schritte
- Versuchen Sie, der Anwendung mit einem anderen
storageKeyein weiteres Ziel hinzuzufügen. - Implementieren Sie in der Benutzeroberfläche die Anzeige des Datums „zuletzt erfüllt“.
- Fügen Sie Animationen oder Konfetti hinzu, wenn die Streak steigt.
Ressourcen
- React Docs: Building Your Own Hooks
- MDN Web Docs: Window.localStorage
- date-fns: A modern JavaScript date utility library
Kurzreferenz
| Aspekt | Wichtige Information |
|---|---|
| Thema | Entwicklung |
| Relevanz | Patientenaufklärung und Bewusstsein |
| Maßnahme | Besprechen Sie dies mit Ihrer Ärztin oder Ihrem Arzt |
| Häufigkeit | Wie von Ihrem Behandlungsteam empfohlen |
| Evidenz | Basierend auf aktuellen klinischen Leitlinien |
Häufig gestellte Fragen
F: Woran erkenne ich, ob das auf mich zutrifft? A: Besprechen Sie Ihre persönliche und familiäre Krankengeschichte mit Ihrer Ärztin oder Ihrem Arzt, um Ihre individuellen Risikofaktoren einzuschätzen und geeignete nächste Schritte festzulegen.
F: Was sollte ich meine Ärztin oder meinen Arzt fragen? A: Gute Fragen sind: „Was bedeuten meine Ergebnisse?“, „Welche Möglichkeiten habe ich?“, „Welche Änderungen des Lebensstils würden helfen?“ und „Wann sollte ich zur Nachkontrolle kommen?“
F: Gibt es Änderungen des Lebensstils, die helfen können? A: Die meisten Gesundheitszustände profitieren von grundlegenden Maßnahmen: ausgewogene Ernährung, regelmäßige körperliche Aktivität (150+ Minuten/Woche), erholsamer Schlaf (7–9 Stunden), Stressbewältigung und Verzicht auf Tabak.
Medizinischer Haftungsausschluss
Dieser Artikel dient ausschließlich Bildungszwecken und stellt keine medizinische Beratung dar. Konsultieren Sie stets eine qualifizierte medizinische Fachperson, bevor Sie Entscheidungen über Ihre Gesundheit, Behandlung oder Versorgung treffen. Die individuellen Umstände unterscheiden sich, und was für eine Person angemessen ist, muss für eine andere nicht angemessen sein.
Wenn Sie einen medizinischen Notfall erleben, rufen Sie sofort 911 (oder Ihre örtliche Notrufnummer) an.