1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import * as React from 'react';
    
  11. import {Fragment, useCallback, useState} from 'react';
    
  12. import ListItem from './ListItem';
    
  13. import styles from './List.css';
    
  14. 
    
  15. export type Item = {
    
  16.   id: number,
    
  17.   isComplete: boolean,
    
  18.   text: string,
    
  19. };
    
  20. 
    
  21. type Props = {};
    
  22. 
    
  23. export default function List(props: Props): React.Node {
    
  24.   const [newItemText, setNewItemText] = useState<string>('');
    
  25.   const [items, setItems] = useState<Array<Item>>([
    
  26.     {id: 1, isComplete: true, text: 'First'},
    
  27.     {id: 2, isComplete: true, text: 'Second'},
    
  28.     {id: 3, isComplete: false, text: 'Third'},
    
  29.   ]);
    
  30.   const [uid, setUID] = useState<number>(4);
    
  31. 
    
  32.   const handleClick = useCallback(() => {
    
  33.     if (newItemText !== '') {
    
  34.       setItems([
    
  35.         ...items,
    
  36.         {
    
  37.           id: uid,
    
  38.           isComplete: false,
    
  39.           text: newItemText,
    
  40.         },
    
  41.       ]);
    
  42.       setUID(uid + 1);
    
  43.       setNewItemText('');
    
  44.     }
    
  45.   }, [newItemText, items, uid]);
    
  46. 
    
  47.   const handleKeyPress = useCallback(
    
  48.     (event: $FlowFixMe) => {
    
  49.       if (event.key === 'Enter') {
    
  50.         handleClick();
    
  51.       }
    
  52.     },
    
  53.     [handleClick],
    
  54.   );
    
  55. 
    
  56.   const handleChange = useCallback(
    
  57.     (event: $FlowFixMe) => {
    
  58.       setNewItemText(event.currentTarget.value);
    
  59.     },
    
  60.     [setNewItemText],
    
  61.   );
    
  62. 
    
  63.   const removeItem = useCallback(
    
  64.     (itemToRemove: $FlowFixMe) =>
    
  65.       setItems(items.filter(item => item !== itemToRemove)),
    
  66.     [items],
    
  67.   );
    
  68. 
    
  69.   const toggleItem = useCallback(
    
  70.     (itemToToggle: $FlowFixMe) => {
    
  71.       // Dont use indexOf()
    
  72.       // because editing props in DevTools creates a new Object.
    
  73.       const index = items.findIndex(item => item.id === itemToToggle.id);
    
  74. 
    
  75.       setItems(
    
  76.         items
    
  77.           .slice(0, index)
    
  78.           .concat({
    
  79.             ...itemToToggle,
    
  80.             isComplete: !itemToToggle.isComplete,
    
  81.           })
    
  82.           .concat(items.slice(index + 1)),
    
  83.       );
    
  84.     },
    
  85.     [items],
    
  86.   );
    
  87. 
    
  88.   return (
    
  89.     <Fragment>
    
  90.       <h1>List</h1>
    
  91.       <input
    
  92.         type="text"
    
  93.         placeholder="New list item..."
    
  94.         className={styles.Input}
    
  95.         value={newItemText}
    
  96.         onChange={handleChange}
    
  97.         onKeyPress={handleKeyPress}
    
  98.       />
    
  99.       <button
    
  100.         className={styles.IconButton}
    
  101.         disabled={newItemText === ''}
    
  102.         onClick={handleClick}>
    
  103.         <span role="img" aria-label="Add item">
    
  104.         </span>
    
  105.       </button>
    
  106.       <ul className={styles.List}>
    
  107.         {items.map(item => (
    
  108.           <ListItem
    
  109.             key={item.id}
    
  110.             item={item}
    
  111.             removeItem={removeItem}
    
  112.             toggleItem={toggleItem}
    
  113.           />
    
  114.         ))}
    
  115.       </ul>
    
  116.     </Fragment>
    
  117.   );
    
  118. }