1. Comece criando a pasta “contexts” e, dentro dela, o arquivo “StockContext.jsx”:

    import { createContext, useState } from "react";
    import PropTypes from "prop-types"
    
    export const StockContext = createContext({})
    
    StockContextProvider.propTypes = {
      children: PropTypes.node
    }
    
    export function StockContextProvider({ children }) {
      const [items, setItems] = useState(() => {
        const storedItems = localStorage.getItem('obc-react-stock')
        if (!storedItems) return []
        const items = JSON.parse(storedItems)
        items.forEach((item) => {
          item.createdAt = new Date(item.createdAt)
          item.updatedAt = new Date(item.updatedAt)
        })
        return items
      })
    
      const addItem = (item) => {
        setItems(current => {
          const updatedItems = [item, ...current]
          localStorage.setItem('obc-react-stock', JSON.stringify(updatedItems))
          return updatedItems
        })
      }
    
      const getItem = (itemId) => {
        return items.find(i => i.id === +itemId)
      }
    
      const updateItem = (itemId, newAttributes) => {
        setItems(current => {
          const itemIndex = current.findIndex(i => i.id === itemId)
          const updatedItems = [...current]
          Object.assign(updatedItems[itemIndex], newAttributes, { updatedAt: new Date() })
          localStorage.setItem('obc-react-stock', JSON.stringify(updatedItems))
          return updatedItems
        })
      }
    
      const deleteItem = (itemId) => {
        setItems(current => {
          const updatedItems = current.filter(item => item.id !== itemId)
          localStorage.setItem('obc-react-stock', JSON.stringify(updatedItems))
          return updatedItems
        })
      }
    
      const stock = {
        items,
        addItem,
        getItem,
        updateItem,
        deleteItem
      }
    
      return (
        <StockContext.Provider value={stock}>
          {children}
        </StockContext.Provider>
      )
    }
    
  2. Crie também a pasta “hooks” e o arquivo “useStock.js”:

    import { useContext } from "react";
    import { StockContext } from "../contexts/StockContext";
    
    export default function useStock() {
      return useContext(StockContext)
    }
    
  3. Crie agora a pasta “components” e, dentro dela, crie o arquivo “ItemsTable.jsx”:

    import { Link } from "react-router-dom";
    import useStock from "../hooks/useStock";
    
    export default function ItemsTable() {
      const { items } = useStock();
    
      return (
        <table>
          <thead>
            <tr>
              <th>ID</th>
              <th>Nome</th>
              <th>Em Estoque</th>
              <th>Categoria</th>
              <th>Ações</th>
            </tr>
          </thead>
          <tbody>
            {items.map((item) => (
              <tr key={item.id}>
                <td>{item.id}</td>
                <td>{item.name}</td>
                <td>{item.quantity} unid.</td>
                <td>{item.category}</td>
                <td>
                  <Link to={`/items/${item.id}`} className="button is-primary is-small">
                    Ver
                  </Link>
                  <Link to={`/items/${item.id}/update`} className="button is-small">
                    Atualizar
                  </Link>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      )
    }
    
  4. Antes de prosseguir, crie uma pasta “entities” e uma classe “StockItem.js” para auxiliar na criação de itens:

    export const CATEGORIES = [
      "Jogos",
      "Livros",
      "Brinquedos",
      "Acessórios"
    ]
    
    export default class StockItem {
      constructor({ name, description, quantity, price, category }) {
        this.id = Math.floor(Math.random() * 10000000)
        this.name = name
        this.description = description
        this.quantity = +quantity
        this.price = +price
        this.category = category
        this.createdAt = new Date()
        this.updatedAt = new Date()
        this.#validate()
      }
    
      #validate() {
        const validName = typeof this.name === "string"
        const validDescription = typeof this.description === "string"
        const validQuantity = typeof this.quantity === "number" && Number.isInteger(this.quantity)
        const validPrice = typeof this.price === "number"
        const validCategory = CATEGORIES.includes(this.category)
        if (!(
          validName &&
          validDescription &&
          validQuantity &&
          validPrice &&
          validCategory
        )) {
          throw new Error("Invalid item!")
        }
      }
    }
    
  5. Agora crie o componente “ItemForm.jsx” na pasta “components”. Ele será usado tanto para criar um novo item quanto para atualizar um item existente:

    import PropTypes from "prop-types"
    import StockItem, { CATEGORIES } from "../entities/StockItem"
    import { useRef, useState } from "react"
    import useStock from "../hooks/useStock"
    
    ItemForm.propTypes = {
      itemToUpdate: PropTypes.object
    }
    
    export default function ItemForm({ itemToUpdate }) {
      const defaultItem = {
        name: "",
        description: "",
        quantity: 0,
        price: 0,
        category: ""
      }
    
      const [item, setItem] = useState(itemToUpdate ? itemToUpdate : defaultItem)
      const { addItem, updateItem } = useStock()
      const inputRef = useRef(null)
    
      const handleChange = (ev) => {
        setItem((current) => ({ ...current, [ev.target.name]: ev.target.value }))
      }
    
      const handleSubmit = (ev) => {
        ev.preventDefault()
        try {
          if (itemToUpdate) {
            updateItem(itemToUpdate.id, item)
            alert("Item atualizado com sucesso!")
          } else {
            const validItem = new StockItem(item)
            addItem(validItem)
            setItem(defaultItem)
            alert("Item cadastrado com sucesso!")
          }
        } catch (err) {
          console.log(err.message)
          alert("Ocorreu um erro.")
        } finally {
          inputRef.current.focus()
        }
      }
    
      return (
        <form onSubmit={handleSubmit}>
          <div className="row">
            <div>
              <label htmlFor="name">Nome</label>
              <input
                type="text"
                name="name"
                id="name"
                required
                ref={inputRef}
                value={item.name}
                onChange={handleChange}
              />
            </div>
            <div>
              <label htmlFor="quantity">Quantidade</label>
              <input
                type="number"
                name="quantity"
                id="quantity"
                required
                min={0}
                step={1}
                value={item.quantity}
                onChange={handleChange}
              />
            </div>
            <div>
              <label htmlFor="price">Preço</label>
              <input
                type="number"
                name="price"
                id="price"
                required
                min={0.00}
                step={0.01}
                value={item.price}
                onChange={handleChange}
              />
            </div>
            <div>
              <label htmlFor="category">Categoria</label>
              <select
                name="category"
                id="category"
                required
                value={item.category}
                onChange={handleChange}
              >
                <option disabled value="">Selecione uma categoria...</option>
                {CATEGORIES.map((category) => (
                  <option
                    key={category}
                    value={category}
                    defaultChecked={item.category === category}
                  >
                    {category}
                  </option>
                ))}
              </select>
            </div>
          </div>
          <div className="form-control">
            <label htmlFor="description">Descrição</label>
            <textarea
              name="description"
              id="description"
              required
              rows={6}
              value={item.description}
              onChange={handleChange}
            ></textarea>
          </div>
          <button className="button is-primary is-large">
            Salvar
          </button>
        </form>
      )
    }
    
  6. Por fim, adicione os componentes de tabela e formulário nas suas respectivas telas:

    src/pages/ListItems.jsx

    import ItemsTable from "../../components/ItemsTable";
    
    export default function ListItems() {
      return <ItemsTable />
    }
    

    src/pages/CreateItem.jsx

    import ItemForm from "../../components/ItemForm";
    
    export default function CreateItem() {
      return <ItemForm />
    }