مشروع عملي: Todo App متكامل — مقدمة React
مشروع Todo App متكامل
سنبني تطبيق مهام متكامل يجمع كل ما تعلمناه.
المميزات
- إضافة/حذف/تعديل المهام
- تصفية حسب الحالة
- حفظ في localStorage
- Dark/Light mode
- تصميم متجاوب
هيكل المشروع
src/
├── components/
│ ├── TodoForm.jsx
│ ├── TodoList.jsx
│ ├── TodoItem.jsx
│ └── FilterButtons.jsx
├── hooks/
│ └── useTodos.js
├── store/
│ └── todoStore.js
├── styles/
│ └── App.css
└── App.jsxStore (Zustand)
// store/todoStore.js
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useTodoStore = create(
persist(
(set) => ({
todos: [],
filter: 'all', // all, active, completed
addTodo: (text) => set((state) => ({
todos: [
...state.todos,
{ id: Date.now(), text, completed: false }
]
})),
toggleTodo: (id) => set((state) => ({
todos: state.todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
)
})),
deleteTodo: (id) => set((state) => ({
todos: state.todos.filter(todo => todo.id !== id)
})),
editTodo: (id, text) => set((state) => ({
todos: state.todos.map(todo =>
todo.id === id ? { ...todo, text } : todo
)
})),
setFilter: (filter) => set({ filter }),
clearCompleted: () => set((state) => ({
todos: state.todos.filter(todo => !todo.completed)
})),
}),
{ name: 'todo-storage' }
)
);// App.jsx الكامل
import { useMemo } from 'react';
import useTodoStore from './store/todoStore';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';
import FilterButtons from './components/FilterButtons';
import './styles/App.css';
function App() {
const { todos, filter, addTodo, clearCompleted } = useTodoStore();
const filteredTodos = useMemo(() => {
switch (filter) {
case 'active':
return todos.filter(t => !t.completed);
case 'completed':
return todos.filter(t => t.completed);
default:
return todos;
}
}, [todos, filter]);
const activeCount = todos.filter(t => !t.completed).length;
return (
<div className="app">
<h1>قائمة المهام</h1>
<TodoForm onAdd={addTodo} />
<FilterButtons
current={filter}
counts={{
all: todos.length,
active: activeCount,
completed: todos.length - activeCount
}}
/>
<TodoList todos={filteredTodos} />
<footer>
<span>{activeCount} مهام متبقية</span>
<button onClick={clearCompleted}>
حذف المكتملة
</button>
</footer>
</div>
);
}
export default App;اختبر فهمك