Stylish Todo App. Add todo items to your list and have a productive day.
- Create
- Read
- Update
- Delete
- Local Storage
- Clone this repo
git clone https://github.com/RamziBach/Todo.git
- Install dependencies
npm install
- Start development server
npm start
Creating
and Updating
onSubmit.
Reading
and Deleting
with array manipulations.
Persistence with Local Storage.
Let's go through how it's built. Excluding the CSS and the Header.
ReactJS
npx create-react-app my-app
uuid
npm i uuid
<div id="root"></div>
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.js
import React from 'react';
import Header from './header/Header';
import Todo from './todo/Todo';
import '../style/app.css';
const App = () => {
return (
<>
<Todo />
</>
);
};
export default App;
Todo.js
Let's break this one down into bite sized pieces.
- Imports π
import React, { useState, useRef, useEffect } from 'react';
import { v4 as uuidv4 } from 'uuid';
- Write your component π
import React, { useState, useRef, useEffect } from 'react';
import { v4 as uuidv4 } from 'uuid';
const Todo = () => {
return null;
};
export default Todo;
- Write your JSX π
import React, { useState, useRef, useEffect } from 'react';
import { v4 as uuidv4 } from 'uuid';
const Todo = () => {
return (
<div>
<h1>Todo</h1>
<form onSubmit={}>
<input
ref={}
onChange={}
value={}
maxLength="40"
placeholder="Enter todo..."
autocomplete="off"
/>
<button>Add todo</button>
<button type="button">Clear</button>
</form>
<ul>
{}
</ul>
</div>
);
};
export default Todo;
- Add state π
const [items, setItems] = useState([]); // Empty array
const [text, setText] = useState('');
const [isEditing, setIsEditing] = useState(false);
const [editId, setEditId] = useState('');
import React, { useState, useRef, useEffect } from 'react';
import { v4 as uuidv4 } from 'uuid';
const Todo = () => {
const [items, setItems] = useState([]);
const [text, setText] = useState('');
const [isEditing, setIsEditing] = useState(false);
const [editId, setEditId] = useState('');
return (
<div>
<h1>Todo</h1>
<form onSubmit={}>
<input
ref={}
onChange={}
value={text} // added text state to value
maxLength="40"
placeholder="Enter todo..."
autocomplete="off"
/>
<button>Add todo</button>
<button type="button">Clear</button>
</form>
<ul>{}</ul>
</div>
);
};
export default Todo;
- Add handle functions π
const handleChange = e => {};
const handleSubmit = e => {};
const handleClear = () => {};
const handleDelete = id => {};
const handleEdit = (id, text) => {};
const handleIsComplete = (id, text, isComplete) => {};
import React, { useState, useRef, useEffect } from 'react';
import { v4 as uuidv4 } from 'uuid';
const Todo = () => {
const [items, setItems] = useState([]);
const [text, setText] = useState('');
const [isEditing, setIsEditing] = useState(false);
const [editId, setEditId] = useState('');
const handleChange = e => {};
const handleSubmit = e => {};
const handleClear = () => {};
const handleDelete = id => {};
const handleEdit = (id, text) => {};
const handleIsComplete = (id, text, isComplete) => {};
return (
<div>
<h1>Todo</h1>
<form onSubmit={handleSubmit}>
{' '}
// Added handleSubmit
<input
ref={}
onChange={handleChange} // Added handleChange
value={text}
maxLength="40"
placeholder="Enter todo..."
autocomplete="off"
/>
<button>Add todo</button>
<button
onClick={handleClear} // Added handleClear
type="button"
>
Clear
</button>
</form>
<ul>{}</ul>
</div>
);
};
export default Todo;
- Write functions π (Hard part...)
// Setting state to the input value dynamically on every change
const handleChange = e => setText(e.target.value);
const handleSubmit = e => {
e.preventDefault(); // Prevent default submit behavior
if (text.length === 0) return; // Stops submission if nothing is typed in input
if (isEditing) {
// Set your state immutably
// This code will execute when we are editing
// It will replace our items state with what it had before
// and make the edit.
setItems(prevState => {
const newItems = [...prevState];
const index = newItems.findIndex(item => item.id === editId);
newItems.splice(index, 1, { id: uuidv4(), text, isComplete: false });
return newItems;
});
setIsEditing(false);
setText('');
setEditId('');
return; // Leave function after edit
}
// Set your state immutably
// Code here will add new items to state
setItems(prevState => [
...prevState,
{ id: uuidv4(), text, isComplete: false },
]);
setText('');
};
const handleClear = () => {
if (isEditing) {
setIsEditing(false);
setText('');
setEditId('');
return;
}
setItems([]); // Deletes everything in our state
};
const handleDelete = id => {
// This returns the previous state but without the index we select.
setItems(prevState => prevState.filter(item => item.id !== id));
};
const handleEdit = (id, text) => {
setIsEditing(true);
setText(text);
setEditId(id); // This is used in handleSubmit
};
const handleIsComplete = (id, text, isComplete) => {
if (isComplete) {
// Immutability
setItems(prevState => {
const newItems = [...prevState];
const index = newItems.findIndex(item => item.id === id);
newItems.splice(index, 1, { id: uuidv4(), text, isComplete: false });
return newItems;
});
return;
}
// Immutability
setItems(prevState => {
const newItems = [...prevState];
const index = newItems.findIndex(item => item.id === id);
newItems.splice(index, 1, { id: uuidv4(), text, isComplete: true });
return newItems;
});
};
- Add ref underneath your state π
const inputRef = useRef(null);
<input
ref={inputRef} // Added ref here
onChange={handleChange}
value={text}
maxLength="40"
placeholder="Enter todo..."
autocomplete="off"
/>
- Add focus to input on Mount π
useEffect(() => inputRef.current.focus(), []);
- Finish JSX π
const mapItems = items.map(item => (
<li key={item.id}>
{item.text}
<button onClick={() => handleDelete(item.id)}>Delete</button>
<button onClick={() => handleEdit(item.id, item.text)}>Edit</button>
<button
onClick={() => handleIsComplete(item.id, item.text, item.isComplete)}
>
Complete
</button>
</li>
));
return (
<div>
<h1>Todo</h1>
<form onSubmit={handleSubmit}>
<input
ref={inputRef}
onChange={handleChange}
value={text}
maxLength="40"
placeholder="Enter todo..."
autocomplete="off"
/>
<button>{isEditing ? 'Update' : `Add #${items.length + 1}`}</button>
<button onClick={handleClear} type="button">
{isEditing ? 'Cancel' : 'Clear'}
</button>
</form>
<ul>{mapItems}</ul>
</div>
);
- Set local storage π
useEffect(() => localStorage.setItem('Items', JSON.stringify(items)), [items]);
- Get local storage π
const LOCAL_STORAGE = () => JSON.parse(localStorage.getItem('Items')) || [];
const [items, setItems] = useState(LOCAL_STORAGE);