Real-world examples of force renders in DraftJS โ€” a.k.a. When/How to use EditorState#forceSelection

ยท

3 min read

EditorState has a static method called forceSelection. It should be used when you want to re-render the editor โ€” with selection and focus โ€” forcefully.

Here is what is said about it in the docs:


๐Ÿ“˜ forceSelection

static forceSelection(
    editorState: EditorState,
    selectionState: SelectionState
): EditorState

Returns a new EditorState object with the specified SelectionState applied, forcing the selection to be rendered.

This is useful when the selection should be manually rendered in the correct location to maintain control of the rendered output.


โœŒ๏ธ A couple of real-world instances of forceSelection usage

1) To fix the focus on undo when the editor state is being set from an external widget

When you set the state from an external widget outside the editor (for instance the + button that appears in empty blocks, in the editor at hashnode.com), the state just before the editor state is set doesn't have focus; and when you do an undo, it causes a faulty selection.

You would do something like the following to handle this case

...
keyBindingFn = keyEvent => {
    const isCommandPlusZ = KeyBindingUtil.hasCommandModifier(
        keyEvent
    );
    if (isCommandPlusZ) {
        const selectionBeforeHasFocus = editorState
            .getCurrentContent()
            .getSelectionBefore()
            .getHasFocus();
        if (!selectionBeforeHasFocus) {
            return 'focus-editor-after-undo';
        }
    }
    return getDefaultKeyBinding(keyEvent);
}

handleKeyCommand = (command) => {
    if (command === 'focus-editor-after-undo') {
        const newEditorState = EditorState.undo(
            this.state.editorState
        );
        this.onChange(
            EditorState.forceSelection(
                newEditorState,
                newEditorState.getSelection()
            )
        );
        return 'handled';
    }
    return NOT_HANDLED;
};
...
render() {
    <Editor
        editorState={this.state.editorState}
        handleKeyCommand={this.handleKeyCommand}
        keyBindingFn={this.keyBindingFn}
        ...
    />
}
...

2) In **draft-js** v0.9, **entities** are decoupled from **contentState**; so the change from updating an entity, doesn't reflect immediately in the editor...

Note: This behaviour is fixed, thanks to new Entity API in draft-js v0.10. Check this migration guide.

If you're working with 0.9 or below, you would have to forcefully render the selection again, a means to re-render the editor, whenever you update an existing entity.

Here's an example, whenever the user uploads an image from the image uploader present inside the + button in the editor at hashnode.com; the image is blurred until it is uploaded to the corresponding CDN; and once that is done, the corresponding entity data is updated. Following is an excerpt from the codebase

uploadUserImage(src)
.then(res => {
    let url = res.image;

    // Preload image, before updating the entity with the new source
    fetch(url)
    .then(() => {
        Entity.mergeData(
            imageEntityKey,
            { src: url, uploaded: true }
        );

        // The following forceSelection needs to be done because
        // the entity data is decoupled from the contentState,
        // and a change in it doesn't trigger a re-render of the editor
        this.onChange(
            EditorState.forceSelection(
                this.state.editorState,
                this.state.editorState.getSelection()
            )
        );
    })
});

๐Ÿ”ฆ Conclusion

As you have seen, you can use forceSelection whenever you want to control the render, selection, and focus in the editor.

Have you gone through any examples where you needed a force render in a DraftJS editor? Let me know! ๐Ÿ™‚