Slate properties props and feature Attributes

In Slate’s documentation, there’s a reminder, “Make sure to mix the attributes of props into the custom component and render props. Children in the custom component.”

The props refer generally to the parameters passed by the parent to the child, the Attributes refer to the built-in features Slate needs during rendering, and the children refer to the text components Slate takes over and is responsible for rendering.

So why is this “attributes” so important? This article will take this question into consideration.

Slate’s custom built-in features

In the process of Slate development, we often see some custom built-in attributes starting with data-, such as data-slate-node. These built-in features are listed below:

Editable

  • data-slate-editorUsed to identify the editor component.

Element

  • Data – SLATE -node: must have values’ element ‘|’ value ‘|’ text ‘, representing elements, document the whole quantity (applicable to the Editable), text node (applicable to isInline elements).

  • Data-slate-void: the value is true if the element is empty; otherwise, it does not exist.

  • Data-slate-inline: The value is true if the element is inline; otherwise, it does not exist.

Additionally, there are the following built-in features in the Attributes for Element:

  • ContentEditable: The value is false if it cannot be edited; otherwise, it does not exist.

  • Dir: The value is’ RTL ‘if the editing direction is from right to left; otherwise, it does not exist.

  • Ref: Mandatory, the ref reference of the current element. Slate adds the mapping between an Element and its corresponding DOM node to the WeakMap of ELEMENT_TO_NODE each time it renders. A lack of ref will result in rendering failures and errors in toSlateNode due to missing mappings in ELEMENT_TO_NODE.

Leaf

  • data-slate-leaf: Yes, the value istrue, indicating that the corresponding DOM element is Leaf node.

String

  • Data-slate-string: The value is true if it is a text node; otherwise, it does not exist.

  • Data – SLATE – zero – width: if zero width of the text node values’ n ‘|’ z ‘, respectively to refer to a newline, not line, otherwise does not exist.

  • Data-slate-length: indicates the actual width of a zero-width text node, in characters. Defaults to 0 or, if not, to the width of the text character of the element with isVoid set.

other

  • data-slate-spacer: set upisVoidElementA layer of elements is wrapped around it that contains the custom properties to distinguish normal elements and to manage the behavior associated with the empty element (copy, cursor focus, cursor out-of-focus, and so on).

Slate’s onion model

The component hierarchy in Slate can be represented as follows:

Slate is essentially an onion model, from the outside to the inside, respectively:

  • SlateAn editor component wrapper used to take overEditableonChangeEvents.
  • EditableIs essentially a superset of Textarea elements, which is also reflected in itsThe parameter typesOn. Is a mutable singleton instance.
  • ChildrenChild component for take overEditablechildrenProperty and is responsible for rendering downElement.
  • ElementElement from the root nodeeditorThe next-level nodes represent element instances, one for each elementtypeAttribute to identify its type. userenderElementMethod to add custom properties and styles. Update in this layerElement node hierarchyThe mapping relation of.
  • TextText component used to take overElementchildrenProperty and is responsible for rendering downLeaf. Update in this layerLeaf node hierarchyThe mapping relation of.
  • LeafLeaves, from the root nodeeditorThe secondary nodes down here, one for each leaftextAttribute toStringRender text. userenderLeafMethod to add custom properties and styles.
  • StringThe lowest level text element, text input and browserDOMWhere the real interaction is, there is no view-data binding with the virtual DOM layer, because this locationcontentEditableDOMNatively supports text input. For example, if you type a character, it will fire once hereonChangeEvent and bubble toSlateTake over processing.

Due to Slate’s Onion model, all elements’ features are directly mounted to the corresponding DOM node, and each corresponding level will have the corresponding attributes built-in features to mark the information of that level node (such as inline elements, This will correspond to data-slate-inline=”true”), such as Element’s built-in feature data-slate-node= “Element”, and Leaf’s built-in feature data-slate-leaf.

Develop a custom component

When a custom component or a custom text node attribute is involved in Slate, the renderLeaf and renderElement of Slate react are used.

Let’s simply develop a custom component to deepen our understanding of the Onion model:

function App() {
  const editor = useMemo(() = > withReact(createEditor()), []);
  const [value, setValue] = useState([
   {
      type: "paragraph".children: [{text: "This is editable ",},],}, {type: "block-quote".children: [{text: "This is block quote ",},],},]);const renderElement = ({ children, element, attributes }) = > {
    return <DefaultElement {.{ children.element.attributes}} / >;
  };

  const DefaultElement = ({ children, element, attributes }) = > {
    if (element.type === "block-quote") {
      return (
        <blockquote style={{ fontFamily: "fantasy}} ">{children}</blockquote>
      );
    }
    return (<div {. attributes} >{children}</div>);
  };

  return (
    <div className="App">
      <Slate editor={editor} value={value} onChange={(val)= > setValue(val)}>
        <Editable renderElement={renderElement} />
      </Slate>
    </div>
  );
}

export default App;
Copy the code

Adding a custom block-Quote component is common, but it is possible to remove the mask from the Onion model and directly return the complete rendering of the block-Quote component as DefaultElement.

We rewrite DefaultElement above as:

  const DefaultElement = ({ children, element, attributes }) = > {
    if (element.type === "block-quote") {
      return (
        <blockquote
          data-slate-node="text"
          ref={attributes.ref}
          style={{ fontFamily: "fantasy}} ">
          <span data-slate-leaf="true" contenteditable="true">
            <span data-slate-string="true">{children[0].props.text.text}</span>
          </span>
        </blockquote>
      );
    }
    return <div {. attributes} >{children}</div>;
  };
Copy the code

The rewritten block-quote component is virtually identical to the DOM structure rendered, returning the component’s rendering directly. The hierarchy fits Slate’s onion model.

Note: This is not recommended in practice, as it would lose the information contained in the leaf node as part of a custom component, and the render results of the leaf node are unpredictable, so doing so may result in inconsistent render results.

In Slate, the weak map ELEMENT_TO_NODE is updated at both the Element and Text levels. The weak map ELEMENT_TO_NODE is not updated at the Element and Text levels. Cannot get the leaf node at path [1] because it refers to a non-leaf node: [object Object]

ToSlateNode error

When using Slate’s React rendering engine, this error is often encountered because of the design limitations of the React layer itself.

Uncaught Error: Cannot resolve a Slate node from DOM node: [object HTMLDivElement]
    at Object.toSlateNode (react-editor.ts:391:1)
    at editable.tsx:761:1
Copy the code

This is because DOM nodes obtained through events do not have corresponding key-value pairs in the ELEMENT_TO_NODE weak mapping, and thus cannot be mapped from DOM elements to corresponding Slate nodes.

In practice, we add a custom data-ignore-slate attribute for a particular node so that nodes with this attribute can be filtered when toSlateNode() is called to avoid errors.

if(domNode? .hasAttribute? . ("data-ignore-slate")) return
Copy the code

conclusion

Starting with Slate attributes, we learned what these built-in features do and how Slate carries information into rendered DOM nodes.

And see how Slate-React wraps data layer by layer, like an onion model, from top to bottom. The data of Slate nodes is managed by hierarchical mapping, which translates layer by layer into DOM nodes on corresponding pages.