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
connectors
Objectselect
(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.
actions
ActionMethodsadd
(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`history
undo
() => 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 within a specified time window (throttleRate).merge
() => ActionMethodsRun an action and merge its changes into the latest recorded history entry. This is useful for combining multiple related changes into a single history record.
query
QueryMethodsgetSerializedNodes
() => 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.
history
canUndo
() => booleanReturns true if undo is possiblecanRedo
() => booleanReturns true if redo is possible
inContext
booleanReturns 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....collected
CollectedThe 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);