Skip to content

Instantly share code, notes, and snippets.

@marekdano
Created March 31, 2020 17:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marekdano/ac2bd147c8144df9500e9e02f8753ab2 to your computer and use it in GitHub Desktop.
Save marekdano/ac2bd147c8144df9500e9e02f8753ab2 to your computer and use it in GitHub Desktop.
Simple Todo list app using React Hooks with CRUD functions edit, delete a todo and/or mark a todo as done.
import React, { useState, useRef, useEffect } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck } from "@fortawesome/free-solid-svg-icons";
import { library } from "@fortawesome/fontawesome-svg-core";
import "./styles.css";
library.add(faCheck);
/*
Todo app structure
TodoApp
- TodoHeader
- TodoList
- TodoListItem #1
- TodoListItem #2
...
- TodoListItem #N
- TodoForm
*/
const TodoList = props => (
<ul className="list-group">
{props.items.map(item => (
<TodoListItem
key={item.index}
item={item}
itemIndex={item.index}
removeItem={props.removeItem}
markTodoDone={props.markTodoDone}
updateItemValue={props.updateItemValue}
/>
))}
</ul>
);
const TodoListItem = ({
item,
itemIndex,
removeItem,
markTodoDone,
updateItemValue
}) => {
const [editMode, setEditMode] = useState(false);
const [inputValue, setInputValue] = useState(item.value);
const handleEditValue = () => {
setEditMode(false);
updateItemValue(inputValue, itemIndex);
};
const keyPress = e => {
if (e.key === "Enter") {
handleEditValue();
}
};
return (
<li className="list-group-item">
<div className={item.done ? "done" : "undone"}>
<FontAwesomeIcon
className="icon"
icon={faCheck}
onClick={() => markTodoDone(itemIndex)}
/>
<span onClick={() => setEditMode(true)}>
{editMode ? (
<input
type="text"
value={inputValue}
onChange={e => setInputValue(e.target.value)}
onBlur={handleEditValue}
onKeyPress={keyPress}
autoFocus
/>
) : (
item.value
)}
</span>
<button
type="button"
className="close"
onClick={() => removeItem(itemIndex)}
>
&times;
</button>
</div>
</li>
);
};
const TodoForm = ({ addItem }) => {
const inputEl = useRef(null);
useEffect(() => {
inputEl.current.focus();
}, []);
const onSubmit = event => {
event.preventDefault();
let newInputValue = inputEl.current.value;
if (newInputValue) {
addItem({ newItemValue: newInputValue });
inputEl.current.value = "";
}
};
return (
<form onSubmit={onSubmit} className="form-inline">
<input
type="text"
ref={inputEl}
className="form-control"
placeholder="add a new todo..."
/>
<button type="submit" className="btn btn-light">
Add
</button>
</form>
);
};
const TodoHeader = () => <h1>Todo list</h1>;
const TodoApp = props => {
const [todoItems, setTodoItems] = useState(props.initItems);
const [id, setId] = useState(props.initItems.length);
const addItem = newTodoItem => {
const newId = id + 1;
setTodoItems([
...todoItems,
{
index: newId,
value: newTodoItem.newItemValue,
done: false
}
]);
setId(newId);
};
const removeItem = itemIndex => {
const updatedTodoItems = todoItems.filter(item => item.index !== itemIndex);
setTodoItems(updatedTodoItems);
};
const markTodoDone = itemIndex => {
const updatedTodoItems = todoItems.map(item =>
item.index === itemIndex
? {
...item,
done: !item.done
}
: item
);
setTodoItems(updatedTodoItems);
};
const updateItemValue = (value, itemIndex) => {
const updatedTodoItems = todoItems.map(item =>
item.index === itemIndex
? {
...item,
value
}
: item
);
setTodoItems(updatedTodoItems);
};
return (
<div id="main">
<TodoHeader />
<TodoList
items={todoItems}
removeItem={removeItem}
markTodoDone={markTodoDone}
updateItemValue={updateItemValue}
/>
<TodoForm addItem={addItem} />
</div>
);
};
export default TodoApp;
@marekdano
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment