I am using the (view, getPos) => … form of the toDom param in Decoration.Widget(). In that function, I am using ReactDOM.Render(). It all works great, except it doesn’t clean up when destroyed. What would be the proper way to detect widget destruction in such a case in order to call react-dom’s unmountComponentAtNode() to clean up?
For now, I am going to see about solving this feature with a NodeView which gives a bit more fidelity to the DOM lifecycle than does a widget.
I guess adding support for a destroy methods to widgets should be possible (it’ll work much like the node view destroy currently works). If you want to submit a patch that adds that, I’ll gladly take a look.
I had the same problem and I solved it by keeping the container of the react component in the plugin state and calling ReactDOM.unmountComponentAtNode(container) every time the decoration is created and re-rendering the widget again in the same container.
// This function returns a function that you can pass to the Decorations.widget
// while memorising the container, so you don't need access to the whole state
const renderWidget = (container) => (view, getPos) => {
ReactDOM.unmountComponentAtNode(container)
ReactDOM.render(<MyComponent />, container)
return container
}
const createDecorations = (container, pos) => {
return [Decoration.widget(pos, renderWidget(container)]
}
new Plugin({
state: {
init(config) {
return {
decorations: DecorationSet.create(config.doc, []),
widgetContainer: document.createElement('div')
}
}
apply(tr, state) {
// If you need to map the decorations
if (...) {
return {
widgetContainer: state.widgetContainer,
decorations: state.decorations.map(tr.mapping, tr.doc, {
onRemove() {
ReactDOM.unmountComponentAtNode(state.widgetContainer)
},
}),
}
}
// Apply logic here and calculate the pos if needed
return {
widgetContainer: state.widgetContainer,
decorations: createDecorations(state.widgetContainer, pos)
}
}
}
})
I don’t think you need to involve the Decoration class directly in this—just WidgetType needs to be able to store a destroy function (from the spec passed to Decoration.widget), and WidetViewDesc needs to call through to that in its destroy method.