mui-tiptap
Version:
A Material-UI (MUI) styled WYSIWYG rich text editor, using Tiptap
51 lines (50 loc) • 2.33 kB
JavaScript
import debounce from "lodash/debounce";
import { Component } from "react";
/**
* This component debounces the rendering of its children.
*
* WARNING: Use with caution! This component should *only* be used when there
* are updates triggered via "force-update" (like via Tiptap's `useEditor` hook
* which updates upon ProseMirror editor changes to the selection, content,
* etc.). For ordinary React components, traditional memoization techniques
* around props and state (like useCallback, useMemo, memo, etc.) should be used
* instead.
*
* This component is provided for a very narrow use-case: with our menu
* controls, without debouncing the controls would re-render per editor state
* change (e.g. for every character typed or for caret movement), which can bog
* things down a bit, like when holding down backspace or typing very quickly.
* (This is due to the way that Tiptap's useEditor re-renders upon changes in
* the ProseMirror state, which is to force-update
* https://github.com/ueberdosis/tiptap/blob/b0198eb14b98db5ca691bd9bfe698ffaddbc4ded/packages/react/src/useEditor.ts#L105-L113,
* rather than in response to prop changes. Because of the force re-render, and
* since we *do* want to watch editor updates, we have to debounce rendering a
* bit less conventionally, rather than using callbacks, memo, etc.). We do
* want/need the menu controls to update very frequently, since we need them to
* reflect the state of the current cursor position and editor nodes/marks,
* etc., but we want rendering to stay performant, so the `wait` and `options`
* defaults below are a reasonable enough balance.
*/
export default class DebounceRender extends Component {
constructor(props) {
var _a, _b;
super(props);
this.updateDebounced = debounce(
// eslint-disable-next-line @typescript-eslint/unbound-method
this.forceUpdate, (_a = props.wait) !== null && _a !== void 0 ? _a : 170, (_b = props.options) !== null && _b !== void 0 ? _b : {
leading: true,
trailing: true,
maxWait: 300,
});
}
shouldComponentUpdate() {
this.updateDebounced();
return false;
}
componentWillUnmount() {
this.updateDebounced.cancel();
}
render() {
return this.props.children;
}
}