Using with React

Hey @andreypopp, good thing you used that reply button so I got the notification to my email.

Nice, I think you got the basis right. The issue with React-based NodeViews isn’t that it’s undoable, it is just that the interfacing & gluing between the NodeViews, Schema and Plugins (with the event dispatching and portals) is quite difficult to make.

So immediately if you want to create a plugin system on top of that implementation, you’ll have to start thinking how to join all the parts (normal plugins + schema + nodeviews (as the React components) + possible toolbar buttons & actions) together. What I got stuck before I got too busy doing other things was the implementing of the event-flow from the actions/key-press plugins to the plugin state, which would then notify the React components through an event-dispatcher. There’s an example in the Atlassian repo if you want to a crack at it.

1 Like

Yeah, I can see that the difficulties are elsewhere.

Btw I’m not sure I want to use portals just yet, the separate roots should probably work fine. Why do you think portals are necessary?

FWIW I don’t think that PM plugin system should be “pure” React, the current abstractions in PM are quite well designed so I intend to keep using the PM API as is. OTOH, nothing prevents from composing a custom editor plugin right inside of a React component based on props.

What I’ve stumbled upon is the problem with nested DOM structures (which will happen quite a lot with React based node views) in NodeView (contentDOM is nested deeply inside dom) and how it’s all parsed by PM on DOM modfications (logged an issue here)

And then I see @johnkueh you’ve done quite a decent starter for the React + PM combo. Very impressive. You got the whole plumbing into a simple React hook, that’s suberb!

If you want to hear my opinion/feedback, I would maybe separate some stuff from the ReactNodeView as it seems a bit cluttered. Also I’m curious about how you use the ReactDOM.createPortal, will the React components be unmounted correctly when they are destroyed? There’s no unmountComponentAtNode() call so I’m not sure does it cause a memory leak.

How in Atlassian’s editor they made it was using a separate portalProvider that was passed down to the ReactNodeView. Also just as I mentioned to andrey, if you want to have the React NodeViews be notified of the plugin state changes you need some sort of event-dispatcher. I was in the middle of devising a react-redux type of addStateToProps HOC, but I was too busy with other stuff. If you have time and interest, you could try that or even create it as a hook. That would be amazing =)! Eg usePluginState(PLUGIN_KEY).

Also does using React’s context add some benefit compared to just passing the NodeView’s attributes down as props to the component? Atlassian’s editor, at some point at least, used extensively context which made the resulting React component tree become like 100 components deep, it seemed like a design mistake.

That’s a good question about the portals. I guess with portals the React nodes will remain in one same React tree, so they can share/use the same React context. And maybe some other advantages, I’m not sure really. I myself noted that Atlassian’s editor used it and it seemed like a use-case that was fit for it.

Yes I agree totally the plugin system shouldn’t React-based per say. It’s just that if you want to have modular plugins that include all those aspects I mentioned, you have to combine them yourself into one custom plugin, as the way you pass nodeviews, plugins etc to PM is through several different APIs.

Eg schema and plugins are provided through editorState yet nodeviews through editorView. Then because editorState is instantiated before the editorView, your plugins won’t have access to it until it is created. So if you are using something like Mobx stores with both the state and the action dispatchers, you need a separate init(editorView) to add the view afterwards. Which is I guess fine but it’s bit awkward. And well I switched from Mobx to Redux since PM kinda forces you to use Redux type application logic.

And oh you also got that problem :sweat_smile:. I think I actually stumbled upon the same thing. In my case I was using lot of inline nodes, so I had to modify the delete functionality to account for the extra offsets with positions. It’s been a long time since I solved it, like over a half of year, so I don’t really remember to exact details. Use the ProseMirror devtools to at least see how the elements behave, and maybe @marijn will tell you how it’s done.

This thread, and also this one https://discuss.prosemirror.net/t/lightweight-react-integration-example/2680 helped me get a very basic editor with persistence to a CQRS-based backend up and running–but with some horrible re-render jank that causes the editablecontent to lose focus.

For those of you using strategies put forth in this thread, can you share your persistence strategy? (both directions)

Hello everyone,

Here is my take on using ProseMirror with React: https://github.com/dminkovsky/use-prosemirror.

It is a minimal, un-opinionated integration that aims to embrace the similarity between the React and ProseMirror render models. It:

  • Separates state and presentation, so you can keep your state as high up as necessary.
  • Allows using EditorView props as React props.
  • Is written in TypeScript.

Feedback and contributions would be most welcome!

7 Likes