A Pen by davidon dan on CodePen.
Created
February 25, 2026 04:14
-
-
Save davidon/38a46aa74c94897ba5ed8c00a5c008f3 to your computer and use it in GitHub Desktop.
App-LocalStorage-Tasks-React
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <div id="root"></div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, { useReducer, useState, useEffect } from 'https://esm.sh/react'; | |
| import ReactDOM from "https://esm.sh/react-dom/client"; // Use /client for React 18+ | |
| import { reducer, getStoredState, TYPES } from 'https://codepen.io/micropresident/pen/jEMOMMJ.js'; | |
| function TaskApp() { | |
| // Initialize useReducer with the data found in LocalStorage | |
| const [state, dispatch] = useReducer(reducer, getStoredState()); | |
| const [text, setText] = useState(""); | |
| // 3. PERSISTENCE LAYER: Save state whenever it changes | |
| useEffect(() => { | |
| localStorage.setItem('task_app_state', JSON.stringify(state)); | |
| console.log("State synced to LocalStorage"); | |
| }, [state]); // Only runs when 'state' object updates | |
| const handleAdd = (e) => { | |
| e.preventDefault(); | |
| if (!text.trim()) return; | |
| dispatch({ type: TYPES.ADD_TASK, payload: text }); | |
| setText(""); | |
| }; | |
| return ( | |
| <div style={{ padding: '20px', maxWidth: '500px', margin: '0 auto' }}> | |
| <h2>Persistent Task Manager</h2> | |
| {/* Metrics Dashboard */} | |
| <section style={metricsStyle}> | |
| <div>Rate: {state.metrics.completionRate}%</div> | |
| <button onClick={() => { | |
| localStorage.removeItem('task_app_state'); | |
| window.location.reload(); // Hard reset | |
| }} style={{fontSize: '10px'}}>Clear Storage</button> | |
| </section> | |
| {/* Input and List (same as previous example) */} | |
| <form onSubmit={handleAdd}> | |
| <input value={text} onChange={(e) => setText(e.target.value)} /> | |
| <button type="submit">Add Task</button> | |
| </form> | |
| <ul> | |
| {state.tasks.map(task => ( | |
| <li key={task.id}> | |
| <input type="checkbox" checked={task.done} onChange={() => dispatch({ type: TYPES.TOGGLE_TASK, payload: task.id })} /> | |
| {task.text} | |
| </li> | |
| ))} | |
| </ul> | |
| </div> | |
| ); | |
| } | |
| const metricsStyle = { display: 'flex', justifyContent: 'space-between', background: '#eee', padding: '10px', marginBottom: '10px' }; | |
| // At the bottom of app.js | |
| const rootElement = document.getElementById("root"); | |
| const root = ReactDOM.createRoot(rootElement); | |
| root.render(<TaskApp />); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment