Estoy construyendo un editor de texto usando React con Typescript. La jerarquía de componentes se ve así: TextEditor -> Blocks -> Block -> ContentEditable.
ContentEditable es un paquete npm https://www.npmjs.com/package/react-contenteditable .
lo que quiero que haga
El comportamiento que busco es similar al editor de texto Medium o Notions. Cuando un usuario escribe en un bloque y presiona enter en su teclado, se debe crear un nuevo bloque después del bloque actual.
Que hace
El comportamiento en este momento es extraño para mí. Si presiono enter y agrego un bloque, funciona bien. Pero si presiono enter nuevamente, anula el bloque anterior en lugar de crear uno nuevo. Sin embargo, si presiono enter y agrego un bloque, luego coloco la zanahoria (enfocando) en el nuevo bloque y presiono enter nuevamente, se agrega un nuevo bloque después de lo esperado.
Salvadera
Aquí hay un sandbox con el código completo: https://codesandbox.io/s/texteditor-mxgbey?file=/src/components/Block.tsx:81-557
Editor de texto
export default function TextEditor(props) { const [blocks, setBlocks] = useState([ { id: "1", tag: "h1", html: "Title1" }, { id: "2", tag: "p", html: "Some text" } ]); function handleAddBlock(id: string) { const index = blocks.findIndex((b) => b.id === id); let copiedBlocks = [...blocks]; let newBlock = { id: nanoid(), tag: "p", html: "New block..." }; copiedBlocks.splice(index + 1, 0, newBlock); setBlocks(copiedBlocks); } return <Blocks injectedBlocks={blocks} handleAddBlock={handleAddBlock} />; }bloques
export default function Blocks(props) { const { injectedBlocks, handleAddBlock } = props; return ( <> {injectedBlocks.map((b) => { return ( <Block key={b.id} id={b.id} tag={b.tag} html={b.html} handleAddBlock={handleAddBlock} /> ); })} </> ); }Bloquear
export default function Block(props) { const { id, tag, html, handleAddBlock } = props; function handleChange(e: React.SyntheticEvent) {} function handleKeyDown(e: React.KeyboardEvent) { if (e.key === "Enter") { console.log("Enter pressed on: ", id); e.preventDefault(); handleAddBlock(id); } } return ( <ContentEditable tagName={tag} html={html} onChange={handleChange} onKeyDown={handleKeyDown} /> ); }El valor del estado no proporciona el valor actualizado mientras la función handleAddBlock llama. Así que usa así,
setBlocks((p) => { let copiedBlocks = [...p]; let newBlock = { id: nanoid(), tag: "p", html: "New block..." }; copiedBlocks.splice(index + 1, 0, newBlock); return copiedBlocks; });Esto le dará el valor de estado actualizado inmediatamente.