Skip to content

Commit

Permalink
refactor(state): replace redux with zustand
Browse files Browse the repository at this point in the history
  • Loading branch information
moklick committed Oct 13, 2021
1 parent e01249a commit ee0d290
Show file tree
Hide file tree
Showing 38 changed files with 791 additions and 901 deletions.
24 changes: 23 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -41,7 +41,8 @@
"react-draggable": "^4.4.4",
"react-redux": "^7.2.5",
"redux": "^4.1.1",
"redux-thunk": "^2.3.0"
"redux-thunk": "^2.3.0",
"zustand": "^3.5.13"
},
"devDependencies": {
"@babel/core": "^7.15.5",
Expand Down
8 changes: 5 additions & 3 deletions src/additional-components/Background/index.tsx
@@ -1,8 +1,8 @@
import React, { memo, useMemo, FC, HTMLAttributes } from 'react';
import cc from 'classcat';

import { useStoreState } from '../../store/hooks';
import { BackgroundVariant } from '../../types';
import { useStore } from '../../store';
import { BackgroundVariant, ReactFlowState } from '../../types';
import { createGridLinesPath, createGridDotsPath } from './utils';

export interface BackgroundProps extends HTMLAttributes<SVGElement> {
Expand All @@ -17,6 +17,8 @@ const defaultColors = {
[BackgroundVariant.Lines]: '#eee',
};

const transformSelector = (s: ReactFlowState) => s.transform;

const Background: FC<BackgroundProps> = ({
variant = BackgroundVariant.Dots,
gap = 15,
Expand All @@ -25,7 +27,7 @@ const Background: FC<BackgroundProps> = ({
style,
className,
}) => {
const [x, y, scale] = useStoreState((s) => s.transform);
const [x, y, scale] = useStore(transformSelector);
// when there are multiple flows on a page we need to make sure that every background gets its own pattern.
const patternId = useMemo(() => `pattern-${Math.floor(Math.random() * 100000)}`, []);

Expand Down
11 changes: 7 additions & 4 deletions src/additional-components/Controls/index.tsx
@@ -1,7 +1,7 @@
import React, { memo, useCallback, HTMLAttributes, FC, useEffect, useState } from 'react';
import cc from 'classcat';

import { useStoreState, useStoreActions } from '../../store/hooks';
import { useStore } from '../../store';

import PlusIcon from '../../../assets/icons/plus.svg';
import MinusIcon from '../../../assets/icons/minus.svg';
Expand All @@ -10,7 +10,7 @@ import LockIcon from '../../../assets/icons/lock.svg';
import UnlockIcon from '../../../assets/icons/unlock.svg';

import useZoomPanHelper from '../../hooks/useZoomPanHelper';
import { FitViewParams } from '../../types';
import { FitViewParams, ReactFlowState } from '../../types';

export interface ControlProps extends HTMLAttributes<HTMLDivElement> {
showZoom?: boolean;
Expand All @@ -31,6 +31,9 @@ export const ControlButton: FC<ControlButtonProps> = ({ children, className, ...
</button>
);

const setInteractiveSelector = (s: ReactFlowState) => s.setInteractive;
const isInteractiveSelector = (s: ReactFlowState) => s.nodesDraggable && s.nodesConnectable && s.elementsSelectable;

const Controls: FC<ControlProps> = ({
style,
showZoom = true,
Expand All @@ -45,10 +48,10 @@ const Controls: FC<ControlProps> = ({
children,
}) => {
const [isVisible, setIsVisible] = useState<boolean>(false);
const setInteractive = useStoreActions((actions) => actions.setInteractive);
const setInteractive = useStore(setInteractiveSelector);
const isInteractive = useStore(isInteractiveSelector);
const { zoomIn, zoomOut, fitView } = useZoomPanHelper();

const isInteractive = useStoreState((s) => s.nodesDraggable && s.nodesConnectable && s.elementsSelectable);
const mapClasses = cc(['react-flow__controls', className]);

const onZoomInHandler = useCallback(() => {
Expand Down
12 changes: 6 additions & 6 deletions src/additional-components/MiniMap/index.tsx
@@ -1,9 +1,9 @@
import React, { memo, HTMLAttributes } from 'react';
import cc from 'classcat';

import { useStoreState } from '../../store/hooks';
import { useStore } from '../../store';
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph';
import { Node, Rect } from '../../types';
import { Node, Rect, ReactFlowState } from '../../types';
import MiniMapNode from './MiniMapNode';

type StringFunc = (node: Node) => string;
Expand All @@ -22,6 +22,8 @@ declare const window: any;
const defaultWidth = 200;
const defaultHeight = 150;

const selector = (s: ReactFlowState) => ({ width: s.width, height: s.height, transform: s.transform, nodes: s.nodes });

const MiniMap = ({
style,
className,
Expand All @@ -32,10 +34,8 @@ const MiniMap = ({
nodeStrokeWidth = 2,
maskColor = 'rgb(240, 242, 243, 0.7)',
}: MiniMapProps) => {
const containerWidth = useStoreState((s) => s.width);
const containerHeight = useStoreState((s) => s.height);
const [tX, tY, tScale] = useStoreState((s) => s.transform);
const nodes = useStoreState((s) => s.nodes);
const { width: containerWidth, height: containerHeight, transform, nodes } = useStore(selector);
const [tX, tY, tScale] = transform;

const mapClasses = cc(['react-flow__minimap', className]);
const elementWidth = (style?.width || defaultWidth)! as number;
Expand Down
14 changes: 3 additions & 11 deletions src/additional-components/ReactFlowProvider/index.tsx
@@ -1,16 +1,8 @@
import React, { FC, useMemo } from 'react';
import { Provider } from 'react-redux';
import React, { FC } from 'react';

import { initialState } from '../../store';
import configureStore from '../../store/configure-store';
import { Provider, createStore } from '../../store';

const ReactFlowProvider: FC = ({ children }) => {
const store = useMemo(() => {
return configureStore(initialState);
}, []);

return <Provider store={store}>{children}</Provider>;
};
const ReactFlowProvider: FC = ({ children }) => <Provider createStore={createStore}>{children}</Provider>;

ReactFlowProvider.displayName = 'ReactFlowProvider';

Expand Down
7 changes: 5 additions & 2 deletions src/components/ConnectionLine/index.tsx
@@ -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 {
Expand All @@ -12,6 +12,7 @@ import {
ConnectionLineType,
ConnectionLineComponent,
HandleType,
ReactFlowState,
} from '../../types';

interface ConnectionLineProps {
Expand All @@ -27,6 +28,8 @@ interface ConnectionLineProps {
CustomConnectionLineComponent?: ConnectionLineComponent;
}

const nodesSelector = (s: ReactFlowState) => s.nodes;

export default ({
connectionNodeId,
connectionHandleId,
Expand All @@ -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;
Expand Down
1 change: 0 additions & 1 deletion src/components/Edges/BezierEdge.tsx
@@ -1,7 +1,6 @@
import React, { memo } from 'react';

import EdgeText from './EdgeText';

import { getMarkerEnd, getCenter } from './utils';
import { EdgeProps, Position } from '../../types';

Expand Down
38 changes: 28 additions & 10 deletions src/components/Edges/wrapEdge.tsx
@@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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(
Expand Down
12 changes: 8 additions & 4 deletions src/components/ElementUpdater/index.tsx
@@ -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);
Expand Down
8 changes: 4 additions & 4 deletions src/components/Handle/handler.ts
@@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit ee0d290

Please sign in to comment.