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) => CollectedA 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
connectorsObjectselect(dom: HTMLElement, nodeId: NodeId) => HTMLElementSpecifies the DOM that when clicked will in turn click the specified Node's user componenthover(dom: HTMLElement, nodeId: NodeId) => HTMLElementSpecifies the DOM that when hovered will in turn hover the specified Node's user componentdrag(dom: HTMLElement, nodeId: NodeId) => HTMLElementSpecifies 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) => HTMLElementSpecifies the DOM that when dragged will create a new instance of the specified User Element at the drop location.
actionsActionMethodsadd(nodes: Node, parentId?: NodeId, index?: number) => voidAdd a Node to the given parent node ID at the specified index. By default the parentId is the id of the Root NodeaddNodeTree(tree: NodeTree, parentId?: NodeId) => voidAdd a NodeTree to the given parent node ID at the specified index. By default the parentId is the id of the Root NodeclearEvents() => voidResets the editors events statedelete(nodeID: NodeId) => voidDelete the specified Nodedeserialize(data: SerializedNodes | string) => voidRecreate Nodes from a SerializedNodes object/json. This will clear all the current Nodes in the editor state with the recreated Nodesmove(nodeId: NodeId, targetParentId: NodeId, index: number) => voidMove a Node to the specified parent Node at the given index.setProp(nodeId: NodeId, update: (props: Object) => void) => voidManipulate the props of the given NodesetCustom(nodeId: NodeId, update: (custom: Object) => void) => voidManipulate the custom values of the given NodesetHidden(nodeId: NodeId, bool: boolean) => voidWhen set to true, the User Component of the specified Node will be hidden, but not removedsetOptions(options: Object) => voidUpdate the editor's options. The options object passed is the same as the <Editor /> props.selectNode(nodeId: NodeId | null) => voidSelect the specified node. You can clear the selection by passing `null`historyundo() => voidUndo the last recorded actionredo() => voidRedo the last undone actionignore() => ActionMethodsRun an action without recording its changes in the historythrottle(throttleRate: number = 500) => ActionMethodsRun 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
queryQueryMethodsgetSerializedNodes() => SerializedNodesReturn the current Nodes into a simpler form safe for storageserialize() => StringReturn getSerializedNodes() in JSONgetOptions() => ObjectGet the options specified in the <Editor /> componentgetDropPlaceholder(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) => NodeHelpersReturns an object containing helper methods to describe the specified Node. Click here for more information.parseReactElement(element: React.ReactElement) => ObjecttoNodeTree(normalize?: (node: Node, jsx: React.ReactElement) => void) => NodeTreeParse a given React element into a NodeTree
parseSerializedNode(node: SerializedNode) => ObjecttoNode(normalize?: (node: Node) => void) => NodeParse a serialized Node back into it's full Node form
parseFreshNode(node: FreshNode) => ObjecttoNode(normalize?: (node: Node) => void) => NodeParse 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.
historycanUndo() => booleanReturns true if undo is possiblecanRedo() => booleanReturns true if redo is possible
inContextbooleanReturns 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....collectedCollectedThe 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) => CollectedA 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)ObjectIdentical 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);