Skip to content

Commit

Permalink
Update editor immediately for native insertion events (#24)
Browse files Browse the repository at this point in the history
Fixes textioHQ/frontend#2259

This is an alternative method to getting around the problem of IE11 having no input event and wanting to allow native browser insertion to increase react performance
See also: facebook#667
See also: facebook#871

The premise of this is that it updates the editor state immediately, on the onBeforeInput event. And then it adds extra flags to know what to do during the render. As opposed to waiting for the input event on IE.
  • Loading branch information
max-winderbaum authored and AnilRedshift committed Feb 8, 2017
1 parent 4e3ab13 commit b8d5379
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 23 deletions.
6 changes: 4 additions & 2 deletions src/component/base/DraftEditor.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ class DraftEditor extends React.Component {
_editor: React.Element<any>;
_placeholderAccessibilityID: string;
_latestEditorState: EditorState;
_pendingStateFromBeforeInput: void | EditorState;
_renderNativeContent: boolean;
_waitingOnInput: boolean;

/**
* Define proxies that can route events to the current handler.
Expand Down Expand Up @@ -480,7 +481,8 @@ class DraftEditor extends React.Component {
* an `onChange` prop to receive state updates passed along from this
* function.
*/
_update(editorState: EditorState): void {
_update(editorState: EditorState, renderNativeContent: boolean = false): void {
this._renderNativeContent = renderNativeContent;
this._latestEditorState = editorState;
this.props.onChange(editorState);
}
Expand Down
32 changes: 16 additions & 16 deletions src/component/handlers/edit/editOnBeforeInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var getEntityKeyForSelection = require('getEntityKeyForSelection');
var isSelectionAtLeafStart = require('isSelectionAtLeafStart');
var nullthrows = require('nullthrows');
var setImmediate = require('setImmediate');
var editOnInput = require('editOnInput');

import type DraftEditor from 'DraftEditor.react';
import type {DraftInlineStyle} from 'DraftInlineStyle';
Expand All @@ -36,6 +37,7 @@ const isEventHandled = require('isEventHandled');
var FF_QUICKFIND_CHAR = '\'';
var FF_QUICKFIND_LINK_CHAR = '\/';
var isFirefox = UserAgent.isBrowser('Firefox');
var isIE = UserAgent.isBrowser('IE');

function mustPreventDefaultForCharacter(character: string): boolean {
return (
Expand Down Expand Up @@ -76,11 +78,6 @@ function replaceText(
* occurs on the relevant text nodes.
*/
function editOnBeforeInput(editor: DraftEditor, e: SyntheticInputEvent): void {
if (editor._pendingStateFromBeforeInput !== undefined) {
editor.update(editor._pendingStateFromBeforeInput);
editor._pendingStateFromBeforeInput = undefined;
}

var chars = e.data;

// In some cases (ex: IE ideographic space insertion) no character data
Expand Down Expand Up @@ -167,17 +164,20 @@ function editOnBeforeInput(editor: DraftEditor, e: SyntheticInputEvent): void {
newEditorState = EditorState.set(newEditorState, {
nativelyRenderedContent: newEditorState.getCurrentContent(),
});
// The native event is allowed to occur. To allow user onChange handlers to
// change the inserted text, we wait until the text is actually inserted
// before we actually update our state. That way when we rerender, the text
// we see in the DOM will already have been inserted properly.
editor._pendingStateFromBeforeInput = newEditorState;
setImmediate(() => {
if (editor._pendingStateFromBeforeInput !== undefined) {
editor.update(editor._pendingStateFromBeforeInput);
editor._pendingStateFromBeforeInput = undefined;
}
});

// Allow the native insertion to occur and update our internal state
// to match. If editor.update() does something like changing a typed
// 'x' to 'abc' in an onChange() handler, we don't want our editOnInput()
// logic to squash that change in favor of the typed 'x'. Set a flag to
// ignore the next editOnInput() event in favor of what's in our internal state.
editor._waitingOnInput = true;
editor.update(newEditorState, true);

if (isIE) {
setImmediate(() => {
editOnInput(editor);
});
}
}
}

Expand Down
11 changes: 8 additions & 3 deletions src/component/handlers/edit/editOnInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ var DOUBLE_NEWLINE = '\n\n';
* due to a spellcheck change, and we can incorporate it into our model.
*/
function editOnInput(editor: DraftEditor): void {
if (editor._pendingStateFromBeforeInput !== undefined) {
editor.update(editor._pendingStateFromBeforeInput);
editor._pendingStateFromBeforeInput = undefined;

// We have already updated our internal state appropriately for this input
// event. See editOnBeforeInput() for more info
if (editor._waitingOnInput) {
if (!editor._renderNativeContent) {
return;
}
editor._waitingOnInput = false;
}

var domSelection = global.getSelection();
Expand Down
3 changes: 1 addition & 2 deletions src/component/handlers/edit/editOnSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ import type DraftEditor from 'DraftEditor.react';

function editOnSelect(editor: DraftEditor): void {
if (editor._blockSelectEvents ||
editor._latestEditorState !== editor.props.editorState ||
editor._pendingStateFromBeforeInput !== undefined) {
editor._latestEditorState !== editor.props.editorState) {
return;
}

Expand Down

0 comments on commit b8d5379

Please sign in to comment.