Slate: Error when calling `focus()` on an empty editor

Created on 21 Sep 2017  Â·  10Comments  Â·  Source: ianstormtaylor/slate

Do you want to request a feature or report a bug?

bug

What's the current behavior?

Calling focus() on an empty editor after it's mounted causes an exception:

Uncaught Error: Invalid `key` argument! It must be either a block, an inline, a text, or a string. You passed: null
    at normalizeKey (https://npmcdn.com/[email protected]/dist/slate.js:14384:9)
    at Document.getFurthestAncestor (https://npmcdn.com/[email protected]/dist/slate.js:12810:13)
    at Document.object.(anonymous function) [as getFurthestAncestor] (https://npmcdn.com/[email protected]/dist/slate.js:19296:28)
    at Document.getSelectionIndexes (https://npmcdn.com/[email protected]/dist/slate.js:13493:26)
    at Content.render (https://npmcdn.com/[email protected]/dist/slate-react.js:8409:30)
    [ ... ]

This was working as recently as 0.24.5, so the breakage must be pretty new.

https://jsfiddle.net/rgrove/sjcc7p0e/2/

kapture 2017-09-21 at 14 47 08

What's the expected behavior?

The editor should be focused.

question ♥ help

Most helpful comment

Why was this closed? We are also running into this.

The solution above doesn't work for me. It will not focus the editor content.

Edit: Ok, it helped to initialise the empty document like this:

if (typeof value === 'undefined' || value === null) {
        return Value.fromJSON({
            document: {
                nodes: [{
                    kind: 'block',
                    type: 'paragraph',
                    nodes: [
                        {
                            kind: 'text',
                            text: '',
                        },
                    ],
                }],
            },
        });
    }

instead of:

return Value.fromJSON({
            document: {
                nodes: [{
                    kind: 'block',
                    type: 'paragraph',
                }],
            },
        });

All 10 comments

Hey @rgrove, in this case there isn't anything to "focus" because there are no nodes in the editor's document. If you change the initial state to have at least one node this should go away.

Actually, maybe you're suggesting that it should not throw an error, which seems very reasonable to me 😄 if you want to PR!

Actually, maybe you're suggesting that it should not throw an error, which seems very reasonable to me 😄 if you want to PR!

Thanks for the guidance!

I took a stab at this, but it looks like focus() was a red herring. It turns out even trying to type in an editor that's initially empty leads to errors at various levels since the initial range's startKey and endKey are always null. I'm not sure if that's expected or not, so I didn't dive in further. Seems easier just to ensure the editor always has at least one node.

It could warn and suggest to write a schema normalization rule.

I don't think this is a red herring.

After upgrading to 0.25.1, I get the same error as the OP whenever I invoke the "focus" function below via the parent element's ref, i.e. this.refs.myEditor.focus()

For some reason, focusing works without error when clicking into the editor or if <p></p> is replaced with <p>a</p>.

Here is the relevant code:

   state = {
      state: html.deserialize('<p></p>')
   };

    focus() {
        const { state } = this.state;
        const change = state.change().focus();
        this.onChange(change);
    }

    onChange = (change) => {
        let {state} = change;
        this.setState({ state })
    };

And a screenshot of the error:
screen shot 2017-09-25 at 1 20 06 am

Any idea why we're seeing this regression?

After further testing, clicking first into the slate editor will ensure that all future focusing via a ref calls will work without errors.

I set a debugger on the slate editor's setState but couldn't find a reason why the pre-click state refuses focus.

I would be interested in what is getting passed into that getFurthestAncestor call, that seems to be where it is dying. I would assume that it's undefined or something and that there neesd to be an edge case for that

@YurkaninRyan a null. When clicking, a "6" is passed into getFurtherestAncestor instead.

I have worked around the issue by replacing the contents of the focus method with:

        setTimeout(() => {
            ReactDOM.findDOMNode(this.refs.editor).focus();
        })

The pre-focus state being unable to accept focus points to a deeper issue which will likely feed into another future bug. Once I learn the ins-and-outs of slate, I can investigate more deeply.

Why was this closed? We are also running into this.

The solution above doesn't work for me. It will not focus the editor content.

Edit: Ok, it helped to initialise the empty document like this:

if (typeof value === 'undefined' || value === null) {
        return Value.fromJSON({
            document: {
                nodes: [{
                    kind: 'block',
                    type: 'paragraph',
                    nodes: [
                        {
                            kind: 'text',
                            text: '',
                        },
                    ],
                }],
            },
        });
    }

instead of:

return Value.fromJSON({
            document: {
                nodes: [{
                    kind: 'block',
                    type: 'paragraph',
                }],
            },
        });

My solution:

    const { editor } = this.props
    if (!editor.value.selection.isFocused) {
      const containerNode = this.containerRef.current
      const documentNode = containerNode.querySelector(`[data-key="${editor.value.document.key}"`)
      documentNode.focus()
    }

Enjoy!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AlexeiAndreev picture AlexeiAndreev  Â·  3Comments

ianstormtaylor picture ianstormtaylor  Â·  3Comments

chrpeter picture chrpeter  Â·  3Comments

ianstormtaylor picture ianstormtaylor  Â·  3Comments

Slapbox picture Slapbox  Â·  3Comments