javascript - Slate.js lanza un error al insertar un nuevo nodo en la región seleccionada
Bloques de código relevantes:
<Slate editor={editor} value={value} onChange={value => {
setValue(value);
const { selection } = editor;
// if nothing is currently selected under the cursor
if (selection && Range.isCollapsed(selection)) {
const [start] = Range.edges(selection);
// if the two characters beforce the cursor are {{, select them and replace with a template block
const before = Editor.before(editor, start, {distance: 2})
const beforeRange = before && Editor.range(editor, before, start)
const beforeText = beforeRange && Editor.string(editor, beforeRange)
const beforeMatch = beforeText && beforeText.match(/\{\{/);
if (beforeMatch) {
Transforms.select(editor, beforeRange as Location);
insertTemplateBlock(editor, {name: "test"})
}
}
}}>
...
</Slate>
export const insertTemplateBlock = (editor: Editor, {name, opts, defaultValue}: TemplateBlockProps) => {
const templateBlock = { type: "template-block", name, opts, defaultValue, children: [{text: ''}] }
Transforms.insertNodes(editor, templateBlock);
Transforms.move(editor);
}
Específicamente, siempre se rompe en la segunda línea dentro de la función insertTemplateBlock, dando un error parecido a:
Cannot find a descendant at path [0,2] in node: {"children":[{"type":"paragraph","children":[{"text":"Thing is working {{"}]}],"operations":[{"type":"insert_text","path":[0,0],"offset":18,"text":"{"},{"type":"set_selection","properties":{"anchor":{"path":[0,0],"offset":19}},"newProperties":{"anchor":{"path":[0,0],"offset":17}}},{"type":"remove_text","path":[0,0],"offset":17,"text":"{{"},{"type":"insert_node","path":[0,1],"node":{"type":"template-block","name":"test","children":[{"text":""}]}},{"type":"set_selection","properties":{"anchor":{"path":[0,0],"offset":17},"focus":{"path":[0,0],"offset":17}},"newProperties":{"anchor":{"path":[0,1,0],"offset":0},"focus":{"path":[0,1,0],"offset":0}}},{"type":"insert_node","path":[0,2],"node":{"text":""}},{"type":"set_selection","properties":{"anchor":{"path":[0,1,0],"offset":0},"focus":{"path":[0,1,0],"offset":0}},"newProperties":{"anchor":{"path":[0,2],"offset":0},"focus":{"path":[0,2],"offset":0}}}],"selection":{"anchor":{"path":[0,2],"offset":0},"focus":{"path":[0,2],"offset":0}},"marks":null,"history":{"undos":[[{"type":"set_selection","properties":null,"newProperties":{"anchor":{"path":[0,0],"offset":0},"focus":{"path":[0,0],"offset":0}}}],[{"type":"insert_text","path":[0,0],"offset":0,"text":"T"},{"type":"insert_text","path":[0,0],"offset":1,"text":"h"},{"type":"insert_text","path":[0,0],"offset":2,"text":"i"},{"type":"insert_text","path":[0,0],"offset":3,"text":"n"},{"type":"insert_text","path":[0,0],"offset":4,"text":"g"},{"type":"insert_text","path":[0,0],"offset":5,"text":" "},{"type":"insert_text","path":[0,0],"offset":6,"text":"i"},{"type":"insert_text","path":[0,0],"offset":7,"text":"s"},{"type":"insert_text","path":[0,0],"offset":8,"text":" "},{"type":"insert_text","path":[0,0],"offset":9,"text":"w"},{"type":"insert_text","path":[0,0],"offset":10,"text":"o"},{"type":"insert_text","path":[0,0],"offset":11,"text":"r"},{"type":"insert_text","path":[0,0],"offset":12,"text":"k"},{"type":"insert_text","path":[0,0],"offset":13,"text":"i"},{"type":"insert_text","path":[0,0],"offset":14,"text":"n"},{"type":"insert_text","path":[0,0],"offset":15,"text":"g"},{"type":"insert_text","path":[0,0],"offset":16,"text":" "},{"type":"set_selection","properties":null,"newProperties":{"anchor":{"path":[0,0],"offset":17},"focus":{"path":[0,0],"offset":17}}}],[{"type":"insert_text","path":[0,0],"offset":17,"text":"{"},{"type":"insert_text","path":[0,0],"offset":18,"text":"{"},{"type":"set_selection","properties":{"anchor":{"path":[0,0],"offset":19}},"newProperties":{"anchor":{"path":[0,0],"offset":17}}},{"type":"remove_text","path":[0,0],"offset":17,"text":"{{"},{"type":"insert_node","path":[0,1],"node":{"type":"template-block","name":"test","children":[{"text":""}]}},{"type":"set_selection","properties":{"anchor":{"path":[0,0],"offset":17},"focus":{"path":[0,0],"offset":17}},"newProperties":{"anchor":{"path":[0,1,0],"offset":0},"focus":{"path":[0,1,0],"offset":0}}},{"type":"insert_node","path":[0,2],"node":{"text":""}},{"type":"set_selection","properties":{"anchor":{"path":[0,1,0],"offset":0},"focus":{"path":[0,1,0],"offset":0}},"newProperties":{"anchor":{"path":[0,2],"offset":0},"focus":{"path":[0,2],"offset":0}}}]],"redos":[]}}
Esto no sucede en su ejemplo de menciones, que utiliza una lógica similar. ¡Cualquier ayuda sería apreciada!
Codesandbox: https://codesandbox.io/s/elastic-turing-dv9bm?file=/src/App.tsx
ACTUALIZACIÓN: Desde entonces he descubierto que este problema se puede evitar moviendo la lógica de inserción (actualmente en la instrucción if) a un hook useEffect dependiendo de un valor target que contenga la selección, de manera similar a cómo funciona el ejemplo de menciones de Slate. Sin embargo, todavía tengo curiosidad por saber por qué Slate se rompe de la forma en que lo hace cuando lo escribo de la manera indicada en la declaración de la pregunta.
Respuesta #1
El problema está relacionado con el renderizado cuando usas useMemo, en su lugar deberías usar useRef:
const editorRef = useRef()
if (!editorRef.current) editorRef.current = withReact(createEditor())
const editor = editorRef.current
https://github.com/ianstormtaylor/slate/issues/4081
Respuesta #2
Creo que tu problema se debe al hecho de que <Slate> es el proveedor de contexto, y en realidad deberías estar usando un componente <Editable> anidado para manejar eventos, en tu caso onKeyDown. En el ejemplo específico que mencionaste, creo que te estás encontrando con una condición de carrera que no ocurrirá con el componente Editable anidado. Consulta Instalando Slate en Guías paso a paso en la documentación.
https://docs.slatejs.org/walkthroughs/01-installing-slate
