function App() { const { Container, Row, Col } = ReactBootstrap; return ( ); } function TodoListCard() { const [items, setItems] = React.useState(null); React.useEffect(() => { fetch('/items') .then(r => r.json()) .then(setItems); }, []); const onNewItem = React.useCallback( newItem => { setItems([...items, newItem]); }, [items], ); const onItemUpdate = React.useCallback( item => { const index = items.findIndex(i => i.id === item.id); setItems([ ...items.slice(0, index), item, ...items.slice(index + 1), ]); }, [items], ); const onItemRemoval = React.useCallback( item => { const index = items.findIndex(i => i.id === item.id); setItems([...items.slice(0, index), ...items.slice(index + 1)]); }, [items], ); if (items === null) return 'Loading...'; return ( {items.length === 0 && (

No items yet! Add one above!

)} {items.map(item => ( ))}
); } function AddItemForm({ onNewItem }) { const { Form, InputGroup, Button } = ReactBootstrap; const [newItem, setNewItem] = React.useState(''); const [submitting, setSubmitting] = React.useState(false); const submitNewItem = e => { e.preventDefault(); setSubmitting(true); fetch('/items', { method: 'POST', body: JSON.stringify({ name: newItem }), headers: { 'Content-Type': 'application/json' }, }) .then(r => r.json()) .then(item => { onNewItem(item); setSubmitting(false); setNewItem(''); }); }; return (
setNewItem(e.target.value)} type="text" placeholder="New Item" aria-describedby="basic-addon1" />
); } function ItemDisplay({ item, onItemUpdate, onItemRemoval }) { const { Container, Row, Col, Button } = ReactBootstrap; const toggleCompletion = () => { fetch(`/items/${item.id}`, { method: 'PUT', body: JSON.stringify({ name: item.name, completed: !item.completed, }), headers: { 'Content-Type': 'application/json' }, }) .then(r => r.json()) .then(onItemUpdate); }; const removeItem = () => { fetch(`/items/${item.id}`, { method: 'DELETE' }).then(() => onItemRemoval(item), ); }; return ( {item.name} ); } ReactDOM.render(, document.getElementById('root'));