Skip to main content

useEditor()

Hook

A Hook that provides methods and state information associated with the entire editor.

const { connectors, actions, query, ...collected } = useEditor(collector);

Reference#

Parameters#

  • collector(state: EditorState, query: Query) => Collected
    A function that collects relevant state information from the editor state. The component will re-render when the values returned by this function changes.

Returns#

  • Object
    • connectorsObject
      • select(dom: HTMLElement, nodeId: NodeId) => HTMLElement
        Specifies the DOM that when clicked will in turn click the specified Node's user component
      • hover(dom: HTMLElement, nodeId: NodeId) => HTMLElement
        Specifies the DOM that when hovered will in turn hover the specified Node's user component
      • drag(dom: HTMLElement, nodeId: NodeId) => HTMLElement
        Specifies the DOM that when dragged will move the specified Node's user component. Only applicable if the component is rendered as an immediate child of a <Canvas /> component.
      • create(dom: HTMLElement, userElement: React.ReactElement) => HTMLElement
        Specifies the DOM that when dragged will create a new instance of the specified User Element at the drop location.
    • actionsActionMethods
      • add(nodes: Node, parentId?: NodeId, index?: number) => void
        Add a Node to the given parent node ID at the specified index. By default the parentId is the id of the Root Node
      • addNodeTree(tree: NodeTree, parentId?: NodeId) => void
        Add a NodeTree to the given parent node ID at the specified index. By default the parentId is the id of the Root Node
      • clearEvents() => void
        Resets the editors events state
      • delete(nodeID: NodeId) => void
        Delete the specified Node
      • deserialize(data: SerializedNodes | string) => void
        Recreate Nodes from a SerializedNodes object/json. This will clear all the current Nodes in the editor state with the recreated Nodes
      • move(nodeId: NodeId, targetParentId: NodeId, index: number) => void
        Move a Node to the specified parent Node at the given index.
      • setProp(nodeId: NodeId, update: (props: Object) => void) => void
        Manipulate the props of the given Node
      • setCustom(nodeId: NodeId, update: (custom: Object) => void) => void
        Manipulate the custom values of the given Node
      • setHidden(nodeId: NodeId, bool: boolean) => void
        When set to true, the User Component of the specified Node will be hidden, but not removed
      • setOptions(options: Object) => void
        Update the editor's options. The options object passed is the same as the <Editor /> props.
      • selectNode(nodeId: NodeId | null) => void
        Select the specified node. You can clear the selection by passing `null`
      • history
        • undo() => void
          Undo the last recorded action
        • redo() => void
          Redo the last undone action
        • ignore() => ActionMethods
          Run an action without recording its changes in the history
        • throttle(throttleRate: number = 500) => ActionMethods
          Run an action while throttling its changes recorded to the history. This is useful if you need to group the changes made by a certain action as a single history record
    • queryQueryMethods
      • getSerializedNodes() => SerializedNodes
        Return the current Nodes into a simpler form safe for storage
      • serialize() => String
        Return getSerializedNodes() in JSON
      • getOptions() => Object
        Get the options specified in the <Editor /> component
      • getDropPlaceholder(sourceNodeId: NodeId, targetNodeId: NodeId, pos: {x: number, y: number}, nodesToDOM?: (node: Node) => HTMLElement = node => node.dom)
        Given the target Node and mouse coordinates on the screen, determine the best possible location to drop the source Node. By default, the Node's DOM property is taken into consideration.
      • node(id: NodeId) => NodeHelpers
        Returns an object containing helper methods to describe the specified Node. Click here for more information.
      • parseReactElement(element: React.ReactElement) => Object
        • toNodeTree(normalize?: (node: Node, jsx: React.ReactElement) => void) => NodeTree
          Parse a given React element into a NodeTree
      • parseSerializedNode(node: SerializedNode) => Object
        • toNode(normalize?: (node: Node) => void) => Node
          Parse a serialized Node back into it's full Node form
      • parseFreshNode(node: FreshNode) => Object
        • toNode(normalize?: (node: Node) => void) => Node
          Parse a fresh/new Node object into it's full Node form, ensuring all properties of a Node is correctly initia lised. This is useful when you need to create a new Node.
      • history
        • canUndo() => boolean
          Returns true if undo is possible
        • canRedo() => boolean
          Returns true if redo is possible
    • inContextboolean
      Returns false if the component is rendered outside of the <Editor />. This is useful if you are designing a general component that you also wish to use outside of Craft.js.
    • ...collectedCollected
      The collected values returned from the collector

Examples#

Collecting state information#

import {useEditor} from "@craftjs/core";
const Example = () => {  const { hoveredNodeId } = useEditor((state) => ({    hoveredNodeId: state.events.hovered  }));
  return (    <div>      The ID of the node currently being hovered is: {hoveredNodeId}    </div>  )}

Updating props#

import {useEditor} from "@craftjs/core";
const Example = () => {  const { selectedNodeId, actions: {setProp} } = useEditor((state) => ({    selectedNodeId: state.events.selected  }));
  return (    <a      onClick={_ => {        setProp(selectedNodeId, props => {          props.text = "new value";        });      }}    >      Update    </a>  )}

Creating new Nodes#

import {useEditor} from "@craftjs/core";
const Example = () => {  const { query, actions } = useEditor((state, query) => ({    hoveredNodeId: state.events.hovered  }));
  return (    <div>      <a onClick={() => {        const nodeTree = query.parseReactElement(<h2>Hi</h2>).toNodeTree();        actions.addNodeTree(nodeTree);      }}>        Add a new Node from a React Element      </a>              <a onClick={() => {        // A fresh Node is a partial Node object        // where only the data.type property is required        const freshNode = {            data: {                type: 'h1'            }        };                // Create a new valid Node object from the fresh Node        const node = query.parseFreshNode(freshNode).toNode();        actions.add(node, 'ROOT');      }}>        Add a new Node from a Node object      </a>    </div>  )}

Hiding and Deleting a Node#

const Example = () => {  const {selectedNodeId, actions} = useEditor((state) => ({    selectedNodeId: state.events.selected  }));  return selectedNodeId && (    <div>      <h2>Node selected: {selectedNodeId}</h2>      <a onClick={() => actions.hide(selectedNodeId)}>Hide</a>      <a onClick={() => actions.delete(selectedNodeId)}>Delete</a>    </div>  )}

Moving a Node#

const Example = () => {  const [sourceId, setSourceId] = useState();  const [targetId, setTargetId] = useState();    const {selectedNodeId, actions, query} = useEditor((state) => ({    selectedNodeId: state.events.selected  }));
  return selectedNodeId && (    <div>      <h2>Node selected: {selectedNodeId}</h2>      <div>        <input type="text" value={sourceId} placeholder="Source" disabled />        <button onClick={() => selectedNodeId && setSourceId(selectedNodeId)}>Set selected Node as source</button>      </div>      <div>        <input type="text" value={targetId} placeholder="Target" disabled />        <button onClick={() => selectedNodeId && setTargetId(selectedNodeId)}>Set selected Node as target</button>      </div>      {        sourceId && targeId ? (          <button onClick={() => {            try {              // .canDropInParent will throw an error message if the conditions failed              query.canDropInParent(sourceId, targetId);               actions.move(sourceId, targetId);            } catch (e) {              console.error(e.message);            }           }}>Move Node</button>        )      }    </div>  )}

Getting the currently selected Node's descendants#

Query methods are also accessible from within the collector function.

import {useEditor} from "@craftjs/core";
const Example = () => {  const { selectedDescendants } = useEditor((state, query) => ({    selectedDescendants: state.events && query.node(state.events.selected).descendants().map(node => node.id)  }));
  return (    <ul>      {        selectedDescendants && selectedDescendants.map(id => <li>{id}</li> )      }    </ul>  )}

Displaying Drop Indicator for the best possible drop location#

const Example = () => {  const [screenClick, setScreenClick] = useState(false);  const [sourceId, setSourceId] = useState();  const [targetId, setTargetId] = useState();    const {selectedNodeId, actions, query} = useEditor((state) => ({    selectedNodeId: state.events.selected  }));
  const disableScreenClick = useEffect((e) => {     if(e.key === "Escape") {       setScreenClick(false);    }  }, [screenClick]);
  const clickOnScreen = useEffect((e) => {    const {clientX: x, clientY: y} = e;    const dropIndicator = query.getDropIndicator(sourceId, targetId, {x, y});    actions.setDropIndicator(dropIndicator);  }, [screenClick]);  
  useEffect(() => {    window.addEventListener("click", clickOnScreen);    window.addEventListener("keyup", disableScreenClick);    return (() => {      window.removeEventListener("click", clickOnScreen);      window.removeEventListener("keyup", disableScreenClick);    })  }, [clickOnScreen, disableScreenClick]);
  return selectedNodeId && (    <div>      <h2>Node selected: {selectedNodeId}</h2>      <div>        <input type="text" value={sourceId} placeholder="Source" disabled />        <button onClick={() => selectedNodeId && setSourceId(selectedNodeId)}>Set selected Node as source</button>      </div>      <div>        <input type="text" value={targetId} placeholder="Target" disabled />        <button onClick={() => selectedNodeId && setTargetId(selectedNodeId)}>Set selected Node as target</button>      </div>      {        sourceId && targeId ? (          <button onClick={() => {            setScreenClick(true);          }}>            {screenClick ? "Click anywhere on the screen to display indicator" : "Start"}          </button>        )      }    </div>  )}

History#

import {useEditor} from "@craftjs/core";
const Example = () => {  const { canUndo, canRedo, actions } = useEditor((state, query) => ({    canUndo: query.history.canUndo(),    canRedo: query.history.canRedo()  }));
  return (    <div>      {        canUndo && <button onClick={() => actions.history.undo()}>Undo</button>      }      {        canRedo && <button onClick={() => actions.history.redo()}>Redo</button>      }
      <button onClick={() => {        // The following action will be ignored by the history        // Hence, it will not be possible to undo/redo the following changes        actions.history.ignore().setProp("ROOT", props => prop.darkMode = !prop.darkMode);      }}>        Toggle      </button>
      <input type="text" onChange={e => {        // In cases where you need to perform an action in rapid successions        // It might be a good idea to throttle the changes        actions.history.throttle().setProp("ROOT", props => props.text = e.target.value);      }} placeholder="Type some text" />    </div>  )}

Legacy API#

For Class Components, use connectEditor instead.

Higher-Order Component

Parameters#

  • collector(node: Node) => Collected
    A function that collects relevant state information from the corresponding Node. The component will re-render when the values returned by this function changes.

Injected Props#

  • ...useEditor(collector)Object
    Identical return values as the useEditor() hook above

Example#

import { connectEditor } from "@craftjs/core";
class SidebarInner extends React.Component {  render() {    const { actions, query, enabled, currentSelectedNodeId } = this.props;    return (      <div>        <input type="checkbox" value={enabled} onChange={          e => actions.setOptions(options => options.enabled = !enabled)        } />        <button           onClick={() => {            console.log(query.serialize())          }}        >            Serialize JSON to console        </button>      </div>    )  }}
export const Sidebar = connectEditor((state) => ({  currentSelectedNodeId: state.events.selected}))(SidebarInner);