Skip to content
/ ToDo Public

The repository serves as a comprehensive implementation of a To-Do List application. According to the README, it is designed using React for building the user interface, TypeScript for ensuring type safety, and Tailwind CSS for styling. The app stores data persistently using the browser's local storage.

License

Notifications You must be signed in to change notification settings

GizzZmo/ToDo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

ToDo

To Do App

Here's a complete Todo List application built with React, TypeScript, and Tailwind CSS, leveraging local storage for persistence.


Key Facts and Principles

  1. React for UI Development: React is a declarative JavaScript library for building user interfaces. It promotes a component-based architecture, where UI elements are broken down into reusable, self-contained components. Key concepts include:

    • Components: Functional components (using hooks) are the standard way to build UI in modern React.
    • State (useState): Manages data that can change over time within a component, triggering re-renders when updated.
    • Side Effects (useEffect): Handles operations that interact with the outside world (e.g., data fetching, DOM manipulation, local storage interaction). It runs after every render by default, but its execution can be controlled by a dependency array.
    • Props: A mechanism for passing data from parent components to child components, enabling data flow down the component tree.
  2. TypeScript for Type Safety: TypeScript is a superset of JavaScript that adds static typing. This helps catch errors during development, improves code readability, and provides better tooling support (e.g., autocompletion, refactoring). It's crucial for building robust and maintainable applications, especially as they scale.

  3. Tailwind CSS for Styling: Tailwind CSS is a utility-first CSS framework. Instead of providing pre-designed components, it offers a vast set of low-level utility classes that can be composed directly in your HTML/JSX to style elements.

    • Utility-First: Rapidly build custom designs without writing custom CSS.
    • Purging: Tailwind CSS can remove unused CSS, resulting in highly optimized and small CSS bundles for production.
    • Configuration: tailwind.config.js allows extensive customization of design tokens (colors, spacing, fonts, etc.).
  4. Local Storage for Data Persistence: localStorage is a web storage API that allows web applications to store data persistently in the browser.

    • Persistence: Data stored in localStorage has no expiration time and remains available even after the browser is closed.
    • Key-Value Pairs: Data is stored as key-value pairs, where both keys and values must be strings.
    • Serialization: To store JavaScript objects or arrays, they must be converted to JSON strings using JSON.stringify() before saving and parsed back using JSON.parse() when retrieving.
    • Limitations: localStorage is synchronous, suitable for small amounts of non-sensitive data, and not ideal for large datasets or complex queries.
    • Integration with React: useEffect is commonly used to synchronize React state with localStorage, loading data on initial render and saving data whenever the state changes.

Project Structure and Explanation

This project will follow a standard React application structure, typically generated by create-react-app or Vite.

todo-app/
├── public/
│   └── index.html
├── src/
│   ├── components/
│   │   ├── TodoItem.tsx
│   │   └── TodoList.tsx
│   ├── utils/
│   │   └── localStorage.ts
│   ├── types/
│   │   └── todo.ts
│   ├── App.tsx
│   ├── index.css
│   └── index.tsx
├── tailwind.config.js
├── postcss.config.js
├── tsconfig.json
├── package.json
└── README.md
  • public/index.html: The main HTML file that serves as the entry point for the React application.
  • src/index.tsx: The entry point for the React application, where the App component is rendered into the DOM.
  • src/App.tsx: The main application component. It manages the global state of todos, including adding, toggling completion, and deleting. It also handles loading and saving todos to local storage.
  • src/components/TodoItem.tsx: A functional component responsible for rendering a single todo item. It receives props for the todo's data and callback functions for handling completion toggles and deletion.
  • src/components/TodoList.tsx: A functional component that receives the array of todos and renders a list of TodoItem components.
  • src/utils/localStorage.ts: Contains utility functions to interact with the browser's localStorage API, specifically for getting and setting the todo list. This centralizes local storage logic.
  • src/types/todo.ts: Defines the TypeScript interface for a Todo object, ensuring type safety throughout the application.
  • src/index.css: The main CSS file where Tailwind CSS directives are imported.
  • tailwind.config.js: Tailwind CSS configuration file, where you define paths to your source files so Tailwind can scan them for utility classes.
  • postcss.config.js: PostCSS configuration, used by Tailwind CSS for processing CSS.
  • tsconfig.json: TypeScript configuration file.
  • package.json: Defines project metadata, scripts, and dependencies.

Complete Code

First, let's assume you've set up a React project with TypeScript and Tailwind CSS. If not, you can typically do this using Vite (recommended for new projects) or Create React App.

Using Vite (Recommended):

  1. npm create vite@latest my-todo-app -- --template react-ts
  2. cd my-todo-app
  3. npm install
  4. npm install -D tailwindcss postcss autoprefixer
  5. npx tailwindcss init -p (This creates tailwind.config.js and postcss.config.js)

Using Create React App:

  1. npx create-react-app my-todo-app --template typescript
  2. cd my-todo-app
  3. npm install -D tailwindcss postcss autoprefixer
  4. npx tailwindcss init -p

Now, let's populate the files:

public/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React Todo App</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/index.tsx"></script>
  </body>
</html>

tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}", // Scan all JS, TS, JSX, TSX files in src
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

src/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

/* Custom styles if any, but keep it minimal for utility-first approach */
body {
  @apply bg-gray-100 font-sans;
}

src/types/todo.ts

export interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

src/utils/localStorage.ts

import { Todo } from '../types/todo';

const LOCAL_STORAGE_KEY = 'react-todo-list';

/**
 * Retrieves the list of todos from local storage.
 * @returns An array of Todo objects, or an empty array if no data is found or an error occurs.
 */
export const getTodos = (): Todo[] => {
  try {
    const storedTodos = localStorage.getItem(LOCAL_STORAGE_KEY);
    return storedTodos ? JSON.parse(storedTodos) : [];
  } catch (error) {
    console.error("Error retrieving todos from local storage:", error);
    return [];
  }
};

/**
 * Saves the list of todos to local storage.
 * @param todos The array of Todo objects to save.
 */
export const saveTodos = (todos: Todo[]): void => {
  try {
    localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(todos));
  } catch (error) {
    console.error("Error saving todos to local storage:", error);
  }
};

src/components/TodoItem.tsx

import React from 'react';
import { Todo } from '../types/todo';

interface TodoItemProps {
  todo: Todo;
  onToggleComplete: (id: string) => void;
  onDelete: (id: string) => void;
}

const TodoItem: React.FC<TodoItemProps> = ({ todo, onToggleComplete, onDelete }) => {
  return (
    <li className="flex items-center justify-between p-4 bg-white border-b border-gray-200 last:border-b-0">
      <div className="flex items-center">
        <input
          type="checkbox"
          checked={todo.completed}
          onChange={() => onToggleComplete(todo.id)}
          className="mr-3 h-5 w-5 text-blue-600 rounded focus:ring-blue-500"
        />
        <span className={`text-lg ${todo.completed ? 'line-through text-gray-500' : 'text-gray-800'}`}>
          {todo.text}
        </span>
      </div>
      <button
        onClick={() => onDelete(todo.id)}
        className="ml-4 p-2 text-sm font-medium text-red-600 hover:text-red-800 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50 rounded-md"
      >
        Delete
      </button>
    </li>
  );
};

export default TodoItem;

src/components/TodoList.tsx

import React from 'react';
import { Todo } from '../types/todo';
import TodoItem from './TodoItem';

interface TodoListProps {
  todos: Todo[];
  onToggleComplete: (id: string) => void;
  onDelete: (id: string) => void;
}

const TodoList: React.FC<TodoListProps> = ({ todos, onToggleComplete, onDelete }) => {
  if (todos.length === 0) {
    return (
      <p className="text-center text-gray-500 mt-8">No todos yet! Add some tasks above.</p>
    );
  }

  return (
    <ul className="bg-white shadow-md rounded-lg overflow-hidden mt-6">
      {todos.map((todo) => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggleComplete={onToggleComplete}
          onDelete={onDelete}
        />
      ))}
    </ul>
  );
};

export default TodoList;

src/App.tsx

import React, { useState, useEffect } from 'react';
import { Todo } from './types/todo';
import { getTodos, saveTodos } from './utils/localStorage';
import TodoList from './components/TodoList';
import { v4 as uuidv4 } from 'uuid'; // For unique IDs

const App: React.FC = () => {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [newTodoText, setNewTodoText] = useState<string>('');

  // Load todos from local storage on initial render
  useEffect(() => {
    setTodos(getTodos());
  }, []);

  // Save todos to local storage whenever the todos state changes
  useEffect(() => {
    saveTodos(todos);
  }, [todos]);

  const handleAddTodo = (e: React.FormEvent) => {
    e.preventDefault();
    if (newTodoText.trim() === '') return;

    const newTodo: Todo = {
      id: uuidv4(), // Generate a unique ID
      text: newTodoText.trim(),
      completed: false,
    };
    setTodos((prevTodos) => [...prevTodos, newTodo]);
    setNewTodoText('');
  };

  const handleToggleComplete = (id: string) => {
    setTodos((prevTodos) =>
      prevTodos.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

  const handleDeleteTodo = (id: string) => {
    setTodos((prevTodos) => prevTodos.filter((todo) => todo.id !== id));
  };

  return (
    <div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
      <div className="bg-white p-8 rounded-lg shadow-xl w-full max-w-md">
        <h1 className="text-4xl font-extrabold text-center text-gray-900 mb-8">
          Todo List
        </h1>

        <form onSubmit={handleAddTodo} className="flex gap-2 mb-6">
          <input
            type="text"
            value={newTodoText}
            onChange={(e) => setNewTodoText(e.target.value)}
            placeholder="Add a new todo..."
            className="flex-grow p-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-lg"
          />
          <button
            type="submit"
            className="px-5 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 text-lg font-semibold"
          >
            Add
          </button>
        </form>

        <TodoList
          todos={todos}
          onToggleComplete={handleToggleComplete}
          onDelete={handleDeleteTodo}
        />
      </div>
    </div>
  );
};

export default App;

src/index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css'; // Import Tailwind CSS

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

package.json (Example for Vite setup)

You'll need to install uuid for unique IDs: npm install uuid @types/uuid.

{
  "name": "my-todo-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "uuid": "^9.0.1"
  },
  "devDependencies": {
    "@types/react": "^18.2.66",
    "@types/react-dom": "^18.2.22",
    "@types/uuid": "^9.0.8",
    "@typescript-eslint/eslint-plugin": "^7.2.0",
    "@typescript-eslint/parser": "^7.2.0",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "autoprefixer": "^10.4.19",
    "eslint": "^8.57.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.6",
    "postcss": "^8.4.38",
    "tailwindcss": "^3.4.3",
    "typescript": "^5.2.2",
    "vite": "^5.2.0"
  }
}

How to Run the Application

  1. Create the Project Directory: If you haven't already, create a new directory for your project and navigate into it.

    mkdir my-todo-app
    cd my-todo-app
  2. Initialize React with TypeScript and Vite:

    npm create vite@latest . -- --template react-ts

    (The . means create in the current directory)

  3. Install Dependencies:

    npm install
  4. Install Tailwind CSS and its dependencies:

    npm install -D tailwindcss postcss autoprefixer
  5. Initialize Tailwind CSS Configuration: This command generates tailwind.config.js and postcss.config.js.

    npx tailwindcss init -p
  6. Install uuid for unique IDs:

    npm install uuid @types/uuid
  7. Copy the Code: Replace the content of the files in your my-todo-app directory with the code provided above, ensuring the correct file paths (src/types/todo.ts, src/utils/localStorage.ts, etc.).

  8. Start the Development Server:

    npm run dev

    Your browser should automatically open to http://localhost:5173/ (or a similar port), displaying the Todo List application.

Now you have a fully functional Todo List application with persistence using local storage, built with React, TypeScript, and Tailwind CSS!

About

The repository serves as a comprehensive implementation of a To-Do List application. According to the README, it is designed using React for building the user interface, TypeScript for ensuring type safety, and Tailwind CSS for styling. The app stores data persistently using the browser's local storage.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published