refactor(state): replace redux with zustand
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState, CSSProperties } from 'react';
|
||||
|
||||
import { useStoreState } from '../../store/hooks';
|
||||
import { useStore } from '../../store';
|
||||
import { getBezierPath } from '../Edges/BezierEdge';
|
||||
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
||||
import {
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
ConnectionLineType,
|
||||
ConnectionLineComponent,
|
||||
HandleType,
|
||||
ReactFlowState,
|
||||
} from '../../types';
|
||||
|
||||
interface ConnectionLineProps {
|
||||
@@ -27,6 +28,8 @@ interface ConnectionLineProps {
|
||||
CustomConnectionLineComponent?: ConnectionLineComponent;
|
||||
}
|
||||
|
||||
const nodesSelector = (s: ReactFlowState) => s.nodes;
|
||||
|
||||
export default ({
|
||||
connectionNodeId,
|
||||
connectionHandleId,
|
||||
@@ -39,7 +42,7 @@ export default ({
|
||||
isConnectable,
|
||||
CustomConnectionLineComponent,
|
||||
}: ConnectionLineProps) => {
|
||||
const nodes = useStoreState((state) => state.nodes);
|
||||
const nodes = useStore(nodesSelector);
|
||||
const [sourceNode, setSourceNode] = useState<Node | null>(null);
|
||||
const nodeId = connectionNodeId;
|
||||
const handleId = connectionHandleId;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import EdgeText from './EdgeText';
|
||||
|
||||
import { getMarkerEnd, getCenter } from './utils';
|
||||
import { EdgeProps, Position } from '../../types';
|
||||
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import React, { memo, ComponentType, useCallback, useState, useMemo } from 'react';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { useStoreActions, useStoreState } from '../../store/hooks';
|
||||
import { Edge, EdgeProps, WrapEdgeProps } from '../../types';
|
||||
import { useStore, useStoreApi } from '../../store';
|
||||
import { Edge, EdgeProps, WrapEdgeProps, ReactFlowState } from '../../types';
|
||||
import { onMouseDown } from '../../components/Handle/handler';
|
||||
import { EdgeAnchor } from './EdgeAnchor';
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
addSelectedElements: s.addSelectedElements,
|
||||
setConnectionNodeId: s.setConnectionNodeId,
|
||||
unsetNodesSelection: s.unsetNodesSelection,
|
||||
setPosition: s.setConnectionPosition,
|
||||
connectionMode: s.connectionMode,
|
||||
});
|
||||
|
||||
export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
const EdgeWrapper = ({
|
||||
id,
|
||||
@@ -47,11 +55,9 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
onEdgeUpdateStart,
|
||||
onEdgeUpdateEnd,
|
||||
}: WrapEdgeProps): JSX.Element | null => {
|
||||
const addSelectedElements = useStoreActions((actions) => actions.addSelectedElements);
|
||||
const setConnectionNodeId = useStoreActions((actions) => actions.setConnectionNodeId);
|
||||
const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection);
|
||||
const setPosition = useStoreActions((actions) => actions.setConnectionPosition);
|
||||
const connectionMode = useStoreState((state) => state.connectionMode);
|
||||
const store = useStoreApi();
|
||||
const { addSelectedElements, setConnectionNodeId, unsetNodesSelection, setPosition, connectionMode } =
|
||||
useStore(selector);
|
||||
|
||||
const [updating, setUpdating] = useState<boolean>(false);
|
||||
|
||||
@@ -90,7 +96,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
(event: React.MouseEvent<SVGGElement, MouseEvent>): void => {
|
||||
if (elementsSelectable) {
|
||||
unsetNodesSelection();
|
||||
addSelectedElements(edgeElement);
|
||||
addSelectedElements([edgeElement]);
|
||||
}
|
||||
|
||||
onClick?.(event, edgeElement);
|
||||
@@ -157,10 +163,22 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
isValidConnection,
|
||||
connectionMode,
|
||||
isSourceHandle ? 'target' : 'source',
|
||||
_onEdgeUpdate
|
||||
_onEdgeUpdate,
|
||||
store.getState
|
||||
);
|
||||
},
|
||||
[id, source, target, type, sourceHandleId, targetHandleId, setConnectionNodeId, setPosition, edgeElement, onConnectEdge]
|
||||
[
|
||||
id,
|
||||
source,
|
||||
target,
|
||||
type,
|
||||
sourceHandleId,
|
||||
targetHandleId,
|
||||
setConnectionNodeId,
|
||||
setPosition,
|
||||
edgeElement,
|
||||
onConnectEdge,
|
||||
]
|
||||
);
|
||||
|
||||
const onEdgeUpdaterSourceMouseDown = useCallback(
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useStoreActions } from '../../store/hooks';
|
||||
import { Node, Edge } from '../../types';
|
||||
import { useStore } from '../../store';
|
||||
import { Node, Edge, ReactFlowState } from '../../types';
|
||||
|
||||
interface ElementUpdaterProps {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
}
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
setNodes: s.setNodes,
|
||||
setEdges: s.setEdges,
|
||||
});
|
||||
|
||||
const ElementUpdater = ({ nodes, edges }: ElementUpdaterProps) => {
|
||||
const setNodes = useStoreActions((actions) => actions.setNodes);
|
||||
const setEdges = useStoreActions((actions) => actions.setEdges);
|
||||
const { setNodes, setEdges } = useStore(selector);
|
||||
|
||||
useEffect(() => {
|
||||
setNodes(nodes);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
||||
import { Store } from 'redux';
|
||||
import { GetState } from 'zustand';
|
||||
|
||||
import { getHostForElement } from '../../utils';
|
||||
import { ReactFlowState } from '../../types';
|
||||
import { ReactFlowAction } from '../../store/actions';
|
||||
|
||||
import {
|
||||
ElementId,
|
||||
@@ -107,7 +106,7 @@ export function onMouseDown(
|
||||
onConnectStart?: OnConnectStartFunc,
|
||||
onConnectStop?: OnConnectStopFunc,
|
||||
onConnectEnd?: OnConnectEndFunc,
|
||||
store?: Store<ReactFlowState, ReactFlowAction>
|
||||
getState?: GetState<ReactFlowState>
|
||||
): void {
|
||||
const reactFlowNode = (event.target as Element).closest('.react-flow');
|
||||
// when react-flow is used inside a shadow root we can't use document
|
||||
@@ -181,7 +180,8 @@ export function onMouseDown(
|
||||
onConnectStop?.(event);
|
||||
|
||||
if (isValid) {
|
||||
onConnect?.(connection, store?.getState().nodes || []);
|
||||
const nodes = getState?.().nodes;
|
||||
onConnect?.(connection, nodes || []);
|
||||
}
|
||||
|
||||
onConnectEnd?.(event);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { memo, useContext, useCallback, HTMLAttributes, forwardRef } from 'react';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { useStoreActions, useStoreState, useStore } from '../../store/hooks';
|
||||
import { useStore } from '../../store';
|
||||
import NodeIdContext from '../../contexts/NodeIdContext';
|
||||
import { HandleProps, Connection, ElementId, Position, Node } from '../../types';
|
||||
import { HandleProps, Connection, ElementId, Position, Node, ReactFlowState } from '../../types';
|
||||
|
||||
import { onMouseDown, SetSourceIdFunc, SetPosition } from './handler';
|
||||
|
||||
@@ -11,6 +11,16 @@ const alwaysValid = () => true;
|
||||
|
||||
export type HandleComponentProps = HandleProps & Omit<HTMLAttributes<HTMLDivElement>, 'id'>;
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
setPosition: s.setConnectionPosition,
|
||||
setConnectionNodeId: s.setConnectionNodeId,
|
||||
onConnectAction: s.onConnect,
|
||||
onConnectStart: s.onConnectStart,
|
||||
onConnectStop: s.onConnectStop,
|
||||
onConnectEnd: s.onConnectEnd,
|
||||
connectionMode: s.connectionMode,
|
||||
});
|
||||
|
||||
const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
(
|
||||
{
|
||||
@@ -26,15 +36,17 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const store = useStore();
|
||||
const nodeId = useContext(NodeIdContext) as ElementId;
|
||||
const setPosition = useStoreActions((actions) => actions.setConnectionPosition);
|
||||
const setConnectionNodeId = useStoreActions((actions) => actions.setConnectionNodeId);
|
||||
const onConnectAction = useStoreState((state) => state.onConnect);
|
||||
const onConnectStart = useStoreState((state) => state.onConnectStart);
|
||||
const onConnectStop = useStoreState((state) => state.onConnectStop);
|
||||
const onConnectEnd = useStoreState((state) => state.onConnectEnd);
|
||||
const connectionMode = useStoreState((state) => state.connectionMode);
|
||||
const {
|
||||
setPosition,
|
||||
setConnectionNodeId,
|
||||
onConnectAction,
|
||||
onConnectStart,
|
||||
onConnectStop,
|
||||
onConnectEnd,
|
||||
connectionMode,
|
||||
} = useStore(selector);
|
||||
|
||||
const handleId = id || null;
|
||||
const isTarget = type === 'target';
|
||||
|
||||
@@ -62,8 +74,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
undefined,
|
||||
onConnectStart,
|
||||
onConnectStop,
|
||||
onConnectEnd,
|
||||
store
|
||||
onConnectEnd
|
||||
);
|
||||
},
|
||||
[
|
||||
|
||||
@@ -8,13 +8,15 @@ const DefaultNode = ({
|
||||
isConnectable,
|
||||
targetPosition = Position.Top,
|
||||
sourcePosition = Position.Bottom,
|
||||
}: NodeProps) => (
|
||||
<>
|
||||
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
|
||||
{data.label}
|
||||
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
|
||||
</>
|
||||
);
|
||||
}: NodeProps) => {
|
||||
return (
|
||||
<>
|
||||
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
|
||||
{data.label}
|
||||
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
DefaultNode.displayName = 'DefaultNode';
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export const getHandleBounds = (nodeElement: HTMLDivElement, scale: number) => {
|
||||
export const getHandleBoundsByHandleType = (
|
||||
selector: string,
|
||||
nodeElement: HTMLDivElement,
|
||||
parentBounds: ClientRect | DOMRect,
|
||||
parentBounds: DOMRect,
|
||||
k: number
|
||||
): HandleElement[] | null => {
|
||||
const handles = nodeElement.querySelectorAll(selector);
|
||||
@@ -24,20 +24,18 @@ export const getHandleBoundsByHandleType = (
|
||||
|
||||
const handlesArray = Array.from(handles) as HTMLDivElement[];
|
||||
|
||||
return handlesArray.map(
|
||||
(handle): HandleElement => {
|
||||
const bounds = handle.getBoundingClientRect();
|
||||
const dimensions = getDimensions(handle);
|
||||
const handleId = handle.getAttribute('data-handleid');
|
||||
const handlePosition = (handle.getAttribute('data-handlepos') as unknown) as Position;
|
||||
return handlesArray.map((handle): HandleElement => {
|
||||
const bounds = handle.getBoundingClientRect();
|
||||
const dimensions = getDimensions(handle);
|
||||
const handleId = handle.getAttribute('data-handleid');
|
||||
const handlePosition = handle.getAttribute('data-handlepos') as unknown as Position;
|
||||
|
||||
return {
|
||||
id: handleId,
|
||||
position: handlePosition,
|
||||
x: (bounds.left - parentBounds.left) / k,
|
||||
y: (bounds.top - parentBounds.top) / k,
|
||||
...dimensions,
|
||||
};
|
||||
}
|
||||
);
|
||||
return {
|
||||
id: handleId,
|
||||
position: handlePosition,
|
||||
x: (bounds.left - parentBounds.left) / k,
|
||||
y: (bounds.top - parentBounds.top) / k,
|
||||
...dimensions,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,9 +2,15 @@ import React, { useEffect, useRef, memo, ComponentType, CSSProperties, useMemo,
|
||||
import { DraggableCore, DraggableData, DraggableEvent } from 'react-draggable';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { useStoreActions, useStoreState } from '../../store/hooks';
|
||||
import { useStore } from '../../store';
|
||||
import { Provider } from '../../contexts/NodeIdContext';
|
||||
import { NodeComponentProps, WrapNodeProps } from '../../types';
|
||||
import { NodeComponentProps, WrapNodeProps, ReactFlowState } from '../../types';
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
addSelectedElements: s.addSelectedElements,
|
||||
onNodesChange: s.onNodesChange,
|
||||
unsetNodesSelection: s.unsetNodesSelection,
|
||||
});
|
||||
|
||||
export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||
const NodeWrapper = ({
|
||||
@@ -41,10 +47,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||
dragHandle,
|
||||
}: WrapNodeProps) => {
|
||||
// const updateNodeDimensions = useStoreActions((actions) => actions.updateNodeDimensions);
|
||||
const addSelectedElements = useStoreActions((actions) => actions.addSelectedElements);
|
||||
const onNodesChange = useStoreState((state) => state.onNodesChange);
|
||||
const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection);
|
||||
|
||||
const { addSelectedElements, onNodesChange, unsetNodesSelection } = useStore(selector);
|
||||
const nodeElement = useRef<HTMLDivElement>(null);
|
||||
|
||||
const node = useMemo(() => ({ id, type, position: { x: xPos, y: yPos }, data }), [id, type, xPos, yPos, data]);
|
||||
@@ -114,7 +117,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||
unsetNodesSelection();
|
||||
|
||||
if (!selected) {
|
||||
addSelectedElements(node);
|
||||
addSelectedElements([node]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +135,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||
unsetNodesSelection();
|
||||
|
||||
if (!selected) {
|
||||
addSelectedElements(node);
|
||||
addSelectedElements([node]);
|
||||
}
|
||||
} else if (!selectNodesOnDrag && !selected && isSelectable) {
|
||||
unsetNodesSelection();
|
||||
@@ -173,7 +176,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||
// Because of that we set dragging to true inside the onDrag handler and handle the click here
|
||||
if (!isDragging) {
|
||||
if (isSelectable && !selectNodesOnDrag && !selected) {
|
||||
addSelectedElements(node);
|
||||
addSelectedElements([node]);
|
||||
}
|
||||
|
||||
onClick?.(event as MouseEvent, node);
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
import React, { useMemo, useCallback, useRef, MouseEvent } from 'react';
|
||||
import ReactDraggable, { DraggableData } from 'react-draggable';
|
||||
|
||||
import { useStoreState, useStoreActions } from '../../store/hooks';
|
||||
import { useStore } from '../../store';
|
||||
import { isNode } from '../../utils/graph';
|
||||
import { Node } from '../../types';
|
||||
import { Node, ReactFlowState } from '../../types';
|
||||
|
||||
export interface NodesSelectionProps {
|
||||
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void;
|
||||
@@ -17,21 +17,34 @@ export interface NodesSelectionProps {
|
||||
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
|
||||
}
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
transform: s.transform,
|
||||
selectedNodesBbox: s.selectedNodesBbox,
|
||||
selectionActive: s.selectionActive,
|
||||
selectedElements: s.selectedElements,
|
||||
snapToGrid: s.snapToGrid,
|
||||
snapGrid: s.snapGrid,
|
||||
nodes: s.nodes,
|
||||
updateNodePosDiff: s.updateNodePosDiff,
|
||||
});
|
||||
|
||||
export default ({
|
||||
onSelectionDragStart,
|
||||
onSelectionDrag,
|
||||
onSelectionDragStop,
|
||||
onSelectionContextMenu,
|
||||
}: NodesSelectionProps) => {
|
||||
const [tX, tY, tScale] = useStoreState((state) => state.transform);
|
||||
const selectedNodesBbox = useStoreState((state) => state.selectedNodesBbox);
|
||||
const selectionActive = useStoreState((state) => state.selectionActive);
|
||||
const selectedElements = useStoreState((state) => state.selectedElements);
|
||||
const snapToGrid = useStoreState((state) => state.snapToGrid);
|
||||
const snapGrid = useStoreState((state) => state.snapGrid);
|
||||
const nodes = useStoreState((state) => state.nodes);
|
||||
|
||||
const updateNodePosDiff = useStoreActions((actions) => actions.updateNodePosDiff);
|
||||
const {
|
||||
transform,
|
||||
selectedNodesBbox,
|
||||
selectionActive,
|
||||
selectedElements,
|
||||
snapToGrid,
|
||||
snapGrid,
|
||||
nodes,
|
||||
updateNodePosDiff,
|
||||
} = useStore(selector);
|
||||
const [tX, tY, tScale] = transform;
|
||||
|
||||
const nodeRef = useRef(null);
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { Elements } from '../../types';
|
||||
import { useStoreState } from '../../store/hooks';
|
||||
import { Elements, ReactFlowState } from '../../types';
|
||||
import { useStore } from '../../store';
|
||||
|
||||
interface SelectionListenerProps {
|
||||
onSelectionChange: (elements: Elements | null) => void;
|
||||
}
|
||||
|
||||
const selectedElementsSelector = (s: ReactFlowState) => s.selectedElements;
|
||||
|
||||
// This is just a helper component for calling the onSelectionChange listener.
|
||||
// As soon as easy-peasy has implemented the effectOn hook, we can remove this component
|
||||
// and use the hook instead. https://github.com/ctrlplusb/easy-peasy/pull/459
|
||||
|
||||
export default ({ onSelectionChange }: SelectionListenerProps) => {
|
||||
const selectedElements = useStoreState((s) => s.selectedElements);
|
||||
const selectedElements = useStore(selectedElementsSelector);
|
||||
|
||||
useEffect(() => {
|
||||
onSelectionChange(selectedElements);
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import { useStoreActions, useStoreState } from '../../store/hooks';
|
||||
import { XYPosition } from '../../types';
|
||||
import { useStore } from '../../store';
|
||||
import { XYPosition, ReactFlowState } from '../../types';
|
||||
|
||||
type UserSelectionProps = {
|
||||
selectionKeyPressed: boolean;
|
||||
@@ -25,8 +25,10 @@ function getMousePosition(event: React.MouseEvent): XYPosition | void {
|
||||
};
|
||||
}
|
||||
|
||||
const userSelectionRectSelector = (state: ReactFlowState) => state.userSelectionRect;
|
||||
|
||||
const SelectionRect = () => {
|
||||
const userSelectionRect = useStoreState((state) => state.userSelectionRect);
|
||||
const userSelectionRect = useStore(userSelectionRectSelector);
|
||||
|
||||
if (!userSelectionRect.draw) {
|
||||
return null;
|
||||
@@ -44,14 +46,25 @@ const SelectionRect = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
selectionActive: s.selectionActive,
|
||||
elementsSelectable: s.elementsSelectable,
|
||||
setUserSelection: s.setUserSelection,
|
||||
updateUserSelection: s.updateUserSelection,
|
||||
unsetUserSelection: s.unsetUserSelection,
|
||||
unsetNodesSelection: s.unsetNodesSelection,
|
||||
});
|
||||
|
||||
export default memo(({ selectionKeyPressed }: UserSelectionProps) => {
|
||||
const selectionActive = useStoreState((state) => state.selectionActive);
|
||||
const elementsSelectable = useStoreState((state) => state.elementsSelectable);
|
||||
const {
|
||||
selectionActive,
|
||||
elementsSelectable,
|
||||
setUserSelection,
|
||||
updateUserSelection,
|
||||
unsetUserSelection,
|
||||
unsetNodesSelection,
|
||||
} = useStore(selector);
|
||||
|
||||
const setUserSelection = useStoreActions((actions) => actions.setUserSelection);
|
||||
const updateUserSelection = useStoreActions((actions) => actions.updateUserSelection);
|
||||
const unsetUserSelection = useStoreActions((actions) => actions.unsetUserSelection);
|
||||
const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection);
|
||||
const renderUserSelectionPane = selectionActive || selectionKeyPressed;
|
||||
|
||||
if (!elementsSelectable || !renderUserSelectionPane) {
|
||||
|
||||
Reference in New Issue
Block a user