plate icon indicating copy to clipboard operation
plate copied to clipboard

`serializeHtml` throws `Invalid hook call` error

Open bfaulk96 opened this issue 2 years ago • 22 comments

Description

When calling serializeHtml (from @udecode/plate-serializer-html), React throws an invalid hook error:

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

Steps to Reproduce

I have a component, CustomPlateEditor. In this component, I have:

<DndProvider backend={HTML5Backend}>
  <Plate editorRef={editorRef} initialValue={initialValue ?? defaultInitialValue} onChange={onChange} plugins={PLATE_PLUGINS}>
    <FixedToolbar>
      <FixedToolbarButtons/>
    </FixedToolbar>
    <Editor focusRing={false} id='PlateEditor' />
    <FloatingToolbar>
      <FloatingToolbarButtons/>
    </FloatingToolbar>
  </Plate>
</DndProvider>

My editorRef is passed down from a parent component, which contains the following code:

export const SomeOtherComponent = () => {
  const editorRef = useRef<PlateEditor | null>(null);
  const [editorValue, setEditorValue] = useState<TElement[]>(defaultInitialValue);
  const [editorHtmlValue, setEditorHtmlValue] = useState('');

  const onEditorChange = (value: TElement[]) => {
    setEditorValue(value);
    if (!editorRef.current) return;

    const html = serializeHtml(editorRef.current, {
      nodes: editorRef.current.children,
      dndWrapper: (props) => <DndProvider backend={HTML5Backend} {...props} />
    });
    setEditorHtmlValue(html);
  };

  // Other logic here

  return <>
    {/* Other stuff here */}
    <CustomPlateEditor editorRef={editorRef} initialValue={editorValue} onChange={onEditorChange} />
  </>
}

When making any sort of change inside my Plate editor, I end up getting the invalid hook error, which I've narrowed down to only happening due to the nodes: editorRef.current.children part of the serializeHtml function. I've tried various other approaches including creating a temporary editor and passing in the editor value, but I get the same React hook error. I've checked that I don't have different versions of React running, so it seems to be an issue within @udecode/plate-serializer-html

Sandbox

I will make a code sandbox within a couple of days and update this issue.

Expected Behavior

I would expect that, with the given code I have in my app, making any change to the Plate rich text editor would result in a raw html string being set for my editorHtmlValue state value.

Environment

Funding

  • You can sponsor this specific effort via a Polar.sh pledge below
  • We receive the pledge once the issue is completed & verified
Fund with Polar

bfaulk96 avatar Dec 13 '23 18:12 bfaulk96

Is there any update?

ningdev1 avatar Jan 20 '24 08:01 ningdev1

Same error. Here is the screenshot. Screenshot 2024-01-24 at 00 14 48

mkmrcodes avatar Jan 23 '24 21:01 mkmrcodes

No ETA until consulting request.

zbeyens avatar Jan 24 '24 10:01 zbeyens

I'm having the same issue.

leonardorb avatar Jan 29 '24 05:01 leonardorb

Having the issue too

juzjus10 avatar Jan 29 '24 06:01 juzjus10

@zbeyens Any solution?

andshonia avatar Feb 06 '24 07:02 andshonia

After thorough debugging over several hours, it was determined that the issue arose from the inclusion of the createBlockSelectionPlugin with specific options, notably:

createBlockSelectionPlugin({ options: { sizes: { top: 0, bottom: 0, }, }, })

The solution involved filtering the array of plugins to exclude the blockSelection plugin:

const excludedSelectionPlugin = plugins?.filter(plugin => plugin?.key !== 'blockSelection');

Subsequently, the modified array excludedSelectionPlugin was passed to the createPlateEditor function:

serializeHtml(createPlateEditor({ plugins: excludedSelectionPlugin }), { nodes: textEditor, data, dndWrapper: props => <DndProvider backend={HTML5Backend} {...props} />, })

This adjustment successfully resolved the encountered issue.

Hope you will figure out the original issue which was caused by 'createBlockSelectionPlugin';

andshonia avatar Feb 06 '24 12:02 andshonia

@andshonia Thank you. Your solution works in my case.

seanbruce avatar Feb 07 '24 01:02 seanbruce

I have the same issue but the workaround is not working for me. It appears that other plugins might also have bug which is causing invalid hook call. Issue happens inside the serializeHtml() call.

shobhit5186 avatar Feb 13 '24 06:02 shobhit5186

image Here have the error message call serializeHtml function. how can I do to resolve this error?

zy-zero avatar Feb 23 '24 07:02 zy-zero

Invalid hook call. Hooks can only be called inside of the body of a function component.

holycrypto avatar Mar 16 '24 03:03 holycrypto

After thorough debugging over several hours, it was determined that the issue arose from the inclusion of the createBlockSelectionPlugin with specific options, notably:

createBlockSelectionPlugin({ options: { sizes: { top: 0, bottom: 0, }, }, })

The solution involved filtering the array of plugins to exclude the blockSelection plugin:

const excludedSelectionPlugin = plugins?.filter(plugin => plugin?.key !== 'blockSelection');

Subsequently, the modified array excludedSelectionPlugin was passed to the createPlateEditor function:

serializeHtml(createPlateEditor({ plugins: excludedSelectionPlugin }), { nodes: textEditor, data, dndWrapper: props => <DndProvider backend={HTML5Backend} {...props} />, })

This adjustment successfully resolved the encountered issue.

Hope you will figure out the original issue which was caused by 'createBlockSelectionPlugin';

I find out, that your solution is not working for me, and made some debugging. now I can See, that createTogglePlugin also causes this bug.

artur1214 avatar Apr 05 '24 10:04 artur1214

I was also facing the same issue:

image

Workaround (that worked for me): createPlateEditor({ plugins: plugins?.filter( (plugin) => plugin?.key !== 'toggle' && plugin?.key !== 'blockSelection' ), })

ankuragrwl avatar Apr 15 '24 18:04 ankuragrwl

By filter out the toggle and blockSelection, the html serialization is now working for me. Furthermore, I do suggest adding context={window} on DndWrapper according to this comment: Multiple DndProviders inside a pure component can lead to Cannot have two HTML5 backends at the same time #3257, if anyone faces cannot have two HTML5Backend problem from DnDWrapper.

Lenghak avatar May 05 '24 08:05 Lenghak

for me filtering toggle and blockSelection did not work. i needed to filter p also

const filteredPlugins = plugins?.filter((plugin) => plugin?.key !== 'toggle' && plugin?.key !== 'blockSelection' && plugin?.key !== 'p');

Latest v34

ajshovon avatar Jun 12 '24 04:06 ajshovon

for me filtering toggle and blockSelection did not work. i needed to filter p also

const filteredPlugins = plugins?.filter((plugin) => plugin?.key !== 'toggle' && plugin?.key !== 'blockSelection' && plugin?.key !== 'p');

Latest v34

I removed the p filtering now it works without it!!!!

But i am facing another problem, i am desearilizing a html and showing it in the editor, but if there is any text, (p tag), the editor get reset if i type something

ajshovon avatar Jun 12 '24 06:06 ajshovon