ARTICLE AD BOX
I have a React + PrimeReact component that displays static text, and when the user clicks an edit button it switches to editable inputs (InputText, InputTextarea, InputNumber, etc.).
The entire component:
import React, { useState, useRef } from "react"; import { Card } from "primereact/card"; import { InputText } from "primereact/inputtext"; import { InputNumber } from "primereact/inputnumber"; import { Button } from "primereact/button"; import { Toast } from "primereact/toast"; import type { Evento } from "../../services/EventoService"; import { atualizarEventoDetalhesGerais } from "../../services/EventoService"; import { formatarMoeda, somarVerbasEvento } from "../../utils/FomatarUtils"; import "../../styles/components/eventos/DetalhesGerais.css"; import { InputTextarea } from "primereact/inputtextarea"; const DetalhesGeraisComponent: React.FC<{ evento: Evento }> = ({ evento }) => { const [editando, setEditando] = useState(false); const [dados, setDados] = useState({ nome: evento.nome, observacoes: evento.observacoes ?? "", codigoBv: evento.codigoBv ?? 0, verbaBv: evento.verbaBv ?? 0, verbaLojas: evento.verbaLojas ?? 0, }); const toast = useRef<Toast>(null); // const handleChange = (campo: keyof typeof dados, valor: string) => { // setDados((prev) => ({ ...prev, [campo]: valor })); // }; const handleChange = ( campo: keyof typeof dados, valor: string | number | null | undefined ) => { setDados((prev) => ({ ...prev, [campo]: valor ?? (typeof prev[campo] === "number" ? 0 : "") })); }; const handleSalvar = async () => { try { // Atualiza no backend const dto = { nome: dados.nome, observacoes: dados.observacoes, codigoBv: dados.codigoBv ? Number(dados.codigoBv) : undefined, verbaBv: dados.verbaBv ?? undefined, verbaLojas: dados.verbaLojas ?? undefined, }; await atualizarEventoDetalhesGerais(evento.id, dto); toast.current?.show({ severity: "success", summary: "Evento atualizado", detail: "As informações foram salvas com sucesso.", life: 2500, }); setEditando(false); } catch (err) { console.error("Erro ao atualizar evento:", err); toast.current?.show({ severity: "error", summary: "Erro ao salvar", detail: "Não foi possível salvar as alterações.", life: 2500, }); } }; return ( <Card title="Informações do Evento" className="card-sessao" style={{ position: "relative" }} onKeyDown={(e) => { if (e.key == "Escape" && editando) setEditando(false); }} > <Toast ref={toast} /> {/* Botão de edição/salvar */} <div style={{ position: "absolute", top: "1rem", right: "1rem" }}> {editando ? ( <> <Button icon="pi pi-check" className="p-button-sm p-button-success" onClick={handleSalvar} title="Salvar alterações" /> <p>"esc" para sair</p> </> ) : ( <Button icon="pi pi-pencil" className="p-button-sm p-button-info" onClick={() => setEditando(true)} title="Editar informações" /> )} </div> <p> <strong>Nome:</strong>{" "} {editando ? ( <InputText value={dados.nome} onChange={(e) => handleChange("nome", e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") handleSalvar(); if (e.key === "Escape") setEditando(false); }} style={{ width: "60%" }} /> ) : ( dados.nome || "—" )} </p> <p> <strong>Observações:</strong>{" "} {editando ? ( <InputTextarea autoResize rows={5} cols={80} value={dados.observacoes} onChange={(e) => handleChange("observacoes", e.target.value)} onKeyDown={(e) => { if (e.key === "Escape") setEditando(false); }} style={{ width: "100%", minHeight: "150px", fontFamily: "inherit", whiteSpace: "pre-wrap", }} /> ) : ( <div style={{ whiteSpace: "pre-wrap", lineHeight: "1.6", marginTop: "0.5rem", padding: "0.5rem 0", }} > {dados.observacoes || "—"} </div> )} </p> <p> <strong>Código BV:</strong>{" "} {editando ? ( <InputNumber value={dados.codigoBv} onValueChange={(e) => handleChange("codigoBv", e.value)} onKeyDown={(e) => { if (e.key === "Enter") handleSalvar(); if (e.key === "Escape") setEditando(false); }} mode="decimal" min={0} maxFractionDigits={0} useGrouping={false} style={{ width: "30%" }} /> ) : dados.codigoBv && Number(dados.codigoBv) > 0 ? ( dados.codigoBv ) : ( <span style={{ color: "red", fontWeight: "bold" }}>Sem Código BV</span> )} </p> {/* Verba BV */} <p> <strong>Verba BV:</strong>{" "} {editando ? ( <InputNumber value={dados.verbaBv} onValueChange={(e) => handleChange("verbaBv", e.value)} onKeyDown={(e) => { if (e.key === "Enter") handleSalvar(); if (e.key === "Escape") setEditando(false); }} mode="currency" currency="BRL" locale="pt-BR" minFractionDigits={2} style={{ width: "30%" }} /> ) : ( formatarMoeda(dados.verbaBv) )} </p> {/* Verba Lojas */} <p> <strong>Verba Lojas:</strong>{" "} {editando ? ( <InputNumber value={dados.verbaLojas} onValueChange={(e) => handleChange("verbaLojas", e.value)} onKeyDown={(e) => { if (e.key === "Enter") handleSalvar(); if (e.key === "Escape") setEditando(false); }} mode="currency" currency="BRL" locale="pt-BR" minFractionDigits={2} style={{ width: "30%" }} /> ) : ( formatarMoeda(dados.verbaLojas) )} </p> <p> <strong>Total verba BV + Verba Lojas:</strong> {formatarMoeda(somarVerbasEvento(evento))} </p> </Card> ); }; export default DetalhesGeraisComponent;This component is used within this another component:
const DetalhesGerais: React.FC<{ evento: Evento }> = ({ evento }) => { return <DetalhesGeraisComponent evento={evento} />; };On Windows everything works perfectly, but on macOS (both Chrome and Safari), clicking the edit button throws this fatal error:
Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. at Px (index-DbgbQxj1.js:48:91035) at _o (...) at Ix (...)After this, the entire app goes blank.
This only happens on MacOs.
The minimal code:
I set the editando (editing) var so the app can render a different tag and the user may edit the data about codeBv, Observations and etc
const [editando, setEditando] = useState(false); return ( <Card> {editando ? ( <InputNumber value={dados.codigoBv} onValueChange={(e) => handleChange("codigoBv", e.value)} /> ) : ( <span>{dados.codigoBv}</span> )} <Button icon="pi pi-pencil" onClick={() => setEditando(true)} /> </Card> );