Draft-js: There is no way to disable undo

Created on 12 Aug 2016  路  16Comments  路  Source: facebook/draft-js

Do you want to request a _feature_ or report a _bug_?
Documentation error and missing function.

What is the current behavior?
The documentation says you should not modify the allowUndo property directly, but instead use the provided static and instance functions. There is not an instance function which allows you to set allowUndo.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar.
The source code says that the _immutable property of the editor state is private and should not be modified, but since there is no other way to do this, thankfully the editor state is just an instance property and can be modified and set.

editorState._immutable = editorState._immutable.set('allowUndo', false);

This is currently the only way to set allowUndo and it's no bueno. Overwriting a private instance property from outside like this is an anti-pattern.

What is the expected behavior?
There should be an instance function to change allowUndo.

Which versions of Draft.js, and which browser / OS are affected by this issue? Did this work in previous versions of Draft.js?
All of them.

Most helpful comment

Update on this, to accomplish this behaviour, one needs to do the following:

editorState = EditorState.set(editorState, {allowUndo: false});
// make changes
editorState = EditorState.set(editorState, {allowUndo: true});

This solution works!

All 16 comments

You seem to mutate the already created "editorState". Instead, return a new "editorState" using the "EditorState" static setter with the new config. As the immutable pattern is intended ;P

Like this

const editorState = EditorState.set(oldEditorState, { allowUndo: false });

You can also do it when creating the editorState using the static method: https://facebook.github.io/draft-js/docs/api-reference-editor-state.html#create

@Pelsin There is no static method for setting allowUndo. The documentation says there is, but there isn't. If the only time you can set it is when you create a brand new editor state, then it's useless. Considering the endless loop bug associated with making an atomic block from an entity (when you undo, it undoes the atomic block conversion, but the entity creation is still there so the blockRendererFn sees the entity and makes the atomic block again - you can never undo the entity creation so you get caught in a loop), having a clean way to disable undo for the first part of that two step process is critical.

allowUndo is supposed to be settable according to the docs, and it needs to be according to basic programming needs. There is only getAllowUndo(). There is no setAllowUndo(). The documentation says there is, but there isn't.

You're welcome to read the code yourself:
https://github.com/facebook/draft-js/blob/master/src/model/immutable/EditorState.js

Further, your example is using an undocumented static function "set", which is on line 105, but is never mentioned in the documentation. One has to wonder why not.

Also, don't need your snarky emotes trying to talk down to me like I don't understand immutability. My example clearly shows I'm using the immutable API. Duh. ;P

This is a documentation issue (it says there's a function but there is is no documentation for it) and it's a missing static function issue (the static function to set allowUndo doesn't exist).

The static function "set" is a general function, it is not specific to allowUndo, and it is undocumented.

You are right, there only seems to be "setInlineStyleOverride" as a way to set one of the defaultRecords.
In the documentation for decorators they mention the use of set for changing decorators on the fly, decorators which is another defaultRecord: https://facebook.github.io/draft-js/docs/advanced-topics-decorators.html#setting-new-decorators

Would agree with you that is not well documented on how to change the defaultRecords.

Not sure where you got the snarky attitude from. The intent was only to help with the title as "There is no way to disable undo", by giving two examples.

Obviously there is _a_ way to disable undo (I provided an example of it myself), it's just not the correct way to do it and the only way to do it is to use undocumented methods even though the documentation explicitly states that there is a way to do it using a static method.

To mark this issue as resolved, a static function to set allowUndo needs to be added to the EditorState.js and the docs need to be updated to include that function.

As far as snark goes, this emoticon reads as snarky:

;P

@Pelsin Guess what I just found out the hard way. Your code example doesn't even work. Did you even try it before you suggested it? Answer: No.

So, my initial assertion was indeed correct. There is no way other than hacking the _immutable property to toggle allowUndo.

@stevensacks Seems to work great for me, made a quick jsfiddle: https://jsfiddle.net/fp9cropq/
Is this not the expected behaviour?

Hope this helps.

It doesn't keep the content of the passed in editorState, so when you use your solution in the root onChange event, every time the editor state is updated, it loses its content. This m eans no content can ever be added. The content state is always cleared out. You can't type, paste or change the content in any way.

Bottom line: Even if you messed with your code to make it work, it's still not the solution to this ticket. A static function is still needed. The documentation is still wrong.

        this.handleChange = editorState => {
            this.setState({editorState});
            // this is a hack to re-enable undo after it was disabled by insertAtomicBlock
            if (!editorState.getAllowUndo())
            {
                const newImmutable = editorState._immutable.set('allowUndo', true);
                editorState._immutable = newImmutable;
            }
        };

If I replace my newImmutable check with your code, the editor allows no content in.

What DraftJS really needs is a way to disable undo for a particular operation like creating an immutable decorated entity which will be turned into an atomic block (since you can't make one yourself, which would be really helpful). In order to fix the un-undoable issue I mentioned above, you have to disable undo before you convert something into a decorated entity, and then AFTER the onChange event fires and the decorated entity is converted into an atomic block you re-enable allowUndo so that one undo will remove the atomic block and entity in one go.

  1. DraftJS documentation says there's a static function to set allowUndo. There isn't and there needs to be.
  2. DraftJS does not allow you to create atomic immutable entities. This is something a lot of people request all the time.
  3. When you use the two-step process where you use the blockRendererFn to convert an immutable entity into an atomic block, the conversion to an atomic block counts as an undo-able action which means you can never undo it. You hit undo, the atomic block is converted back to an entity. The blockRendererFn runs again, sees the entity it needs to convert into an atomic block and converts it. You can never get past this. Ever.

Since you cannot do #2, you need to disable undo during the entity creation step, update the editor state, enable undo again, and then the blockRendererFn will run and convert your entity into an atomic block. When you undo, it will remove the atomic block because the entity creation was not part of the undo stack.

I am having a similar problem. I can't seem to be able to do EditorState.push without it going onto the undoStack. I'm using 0.10 and my code is:

const {allowUndo} = EditorState;

EditorState.allowUndo = false;
const editorState = EditorState.push(editorState, contentState, 'change-inline-style');
EditorState.allowUndo = allowUndo;

There is some text being highlighted in my case and I don't want that to be part of the undo/redo stack.

Are there any updates on this? /cc @stevensacks

Update on this, to accomplish this behaviour, one needs to do the following:

editorState = EditorState.set(editorState, {allowUndo: false});
// make changes
editorState = EditorState.set(editorState, {allowUndo: true});

This solution works!

Just to provide some closure to this issue:

The documentation says you should not modify the allowUndo property directly, but instead use the provided static and instance functions. There is not an instance function which allows you to set allowUndo.

What we should make more clear here is that "static and instance functions" means use "set" to pass new options to an EditorState instance, and then as with all Immutable data structures you get back a new instance. Thanks @gbbr for the example of how to do that above.

I'd love to get help make the docs clearer, but I'm opening a new issue for that - https://github.com/facebook/draft-js/issues/1445

Was this page helpful?
0 / 5 - 0 ratings