0

I would like to have my text editor display how many times a content block has been selected and executed though a key command. I'm doing this by applying an entity to the selected block with a evaluatedTimes property. The data is changed correctly but the entity component doesn't re-render until I insert new characters in the block of text.

The decorator strategy of the entity doesn't get called, so the only way out I found is to update the editor state with a new decorator instance. This way the entity components get re-rendered but the solution fills a bit hacky.

dependencies:

"draft-js": "^0.11.3",
"draft-js-plugins-editor": "^3.0.0",
"draftjs-utils": "^0.10.2"

code:

// plugin.js

import { EditorState } from "draft-js";
import { getSelectionText, getSelectionEntity } from "draftjs-utils";
import { EvaluatedSpan } from "./components";
import { findEvaluatedEntities, createEvaluatedEntity } from "./entities";

export function createCodeEvaluationPlugin({ onEvaluate = () => {} }) {
  return {
    decorators: [
      {
        strategy: findEvaluatedEntities,
        component: EvaluatedSpan
      }
    ],

    keyBindingFn: e => {
      // CMD + ENTER
      if (e.metaKey && e.keyCode === 13) {
        return "evaluate";
      }
    },

    handleKeyCommand: (command, editorState, _, { setEditorState }) => {
      if (command === "evaluate") {
        const selectionState = editorState.getSelection();
        const contentState = editorState.getCurrentContent();

        const entityKey = getSelectionEntity(editorState);

        // If selection contains an entity:
        if (!entityKey) {
          // Create new entity and update editor.
          setEditorState(createEvaluatedEntity(editorState, selectionState));
        } else {
          // Modify entity data.
          const entity = contentState.getEntity(entityKey);
          const nextContentState = contentState.mergeEntityData(entityKey, {
            evaluatedTimes: entity.data.evaluatedTimes + 1
          });

          // Update editor.
          setEditorState(
            EditorState.push(editorState, nextContentState, "change-block-data")
          );
        }

        // Pass text to callback handle
        const selectionText = getSelectionText(editorState);
        onEvaluate(selectionText);

        return "handled";
      }
      return "not-handled";
    }
  };
}

  • I also just ran into this. I would have assumed given the nature of draftjs immutable data, any change to the content would trigger an on change. – Paul Sachs Jan 14 '21 at 23:37

2 Answers2

0

According to this (https://github.com/facebook/draft-js/issues/1702) you could also update the selection state, which may be sufficient for you.

Paul Sachs
  • 905
  • 2
  • 11
  • 22
0

Works for me:

function forceRerender(editorState: EditorState): EditorState {
    const selectionState = editorState.getSelection();
    return EditorState.forceSelection(editorState, selectionState);
}
Diamant
  • 51
  • 3