refactor: reduce menu bar rerenders (#7589)

* reduce menu bar rerenders

* imports cleanup

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Mike Fortman 2025-04-15 11:20:43 -05:00 committed by GitHub
commit 2c2a348a2f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 73 additions and 31 deletions

View file

@ -4,7 +4,6 @@ import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import useAddFlow from "@/hooks/flows/use-add-flow";
import useSaveFlow from "@/hooks/flows/use-save-flow";
import useUploadFlow from "@/hooks/flows/use-upload-flow";
import { customStringify } from "@/utils/reactflowUtils";
import { useHotkeys } from "react-hotkeys-hook";
import IconComponent from "@/components/common/genericIconComponent";
@ -22,6 +21,7 @@ import { UPLOAD_ERROR_ALERT } from "@/constants/alerts_constants";
import { SAVED_HOVER } from "@/constants/constants";
import { useGetRefreshFlowsQuery } from "@/controllers/API/queries/flows/use-get-refresh-flows-query";
import { useGetFoldersQuery } from "@/controllers/API/queries/folders/use-get-folders";
import { useUnsavedChanges } from "@/hooks/useUnsavedChanges";
import ExportModal from "@/modals/exportModal";
import FlowLogsModal from "@/modals/flowLogsModal";
import FlowSettingsModal from "@/modals/flowSettingsModal";
@ -33,6 +33,7 @@ import { useShortcutsStore } from "@/stores/shortcuts";
import { swatchColors } from "@/utils/styleUtils";
import { cn, getNumberFromString } from "@/utils/utils";
import { useQueryClient } from "@tanstack/react-query";
import { useShallow } from "zustand/react/shallow";
export const MenuBar = ({}: {}): JSX.Element => {
const shortcuts = useShortcutsStore((state) => state.shortcuts);
@ -50,18 +51,36 @@ export const MenuBar = ({}: {}): JSX.Element => {
const saveFlow = useSaveFlow();
const queryClient = useQueryClient();
const autoSaving = useFlowsManagerStore((state) => state.autoSaving);
const currentFlow = useFlowStore((state) => state.currentFlow);
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
const updatedAt = currentSavedFlow?.updated_at;
const {
currentFlowName,
currentFlowId,
currentFlowFolderId,
currentFlowIcon,
currentFlowGradient,
} = useFlowStore(
useShallow((state) => ({
currentFlowName: state.currentFlow?.name,
currentFlowId: state.currentFlow?.id,
currentFlowFolderId: state.currentFlow?.folder_id,
currentFlowIcon: state.currentFlow?.icon,
currentFlowGradient: state.currentFlow?.gradient,
})),
);
const { updated_at: updatedAt } = useFlowsManagerStore(
useShallow((state) => ({
updated_at: state.currentFlow?.updated_at,
})),
);
const onFlowPage = useFlowStore((state) => state.onFlowPage);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
const stopBuilding = useFlowStore((state) => state.stopBuilding);
const [editingName, setEditingName] = useState(false);
const [flowName, setFlowName] = useState(currentFlow?.name ?? "");
const [flowName, setFlowName] = useState(currentFlowName ?? "");
const [isInvalidName, setIsInvalidName] = useState(false);
const nameInputRef = useRef<HTMLInputElement>(null);
const [inputWidth, setInputWidth] = useState<number>(0);
const measureRef = useRef<HTMLSpanElement>(null);
const changesNotSaved = useUnsavedChanges();
const { data: folders, isFetched: isFoldersFetched } = useGetFoldersQuery();
const flows = useFlowsManagerStore((state) => state.flows);
@ -73,9 +92,9 @@ export const MenuBar = ({}: {}): JSX.Element => {
flows.forEach((flow) => {
tempNameList.push(flow.name);
});
setNameList(tempNameList.filter((name) => name !== currentFlow?.name));
setNameList(tempNameList.filter((name) => name !== currentFlowName));
}
}, [flows, currentFlow?.name]);
}, [flows, currentFlowName]);
useGetRefreshFlowsQuery(
{
@ -86,13 +105,10 @@ export const MenuBar = ({}: {}): JSX.Element => {
);
const currentFolder = useMemo(
() => folders?.find((f) => f.id === currentFlow?.folder_id),
[folders, currentFlow?.folder_id],
() => folders?.find((f) => f.id === currentFlowFolderId),
[folders, currentFlowFolderId],
);
const changesNotSaved =
customStringify(currentFlow) !== customStringify(currentSavedFlow);
useEffect(() => {
if (measureRef.current) {
setInputWidth(measureRef.current.offsetWidth);
@ -163,26 +179,29 @@ export const MenuBar = ({}: {}): JSX.Element => {
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Escape") {
setEditingName(false);
setFlowName(currentFlow?.name ?? "");
setFlowName(currentFlowName ?? "");
setIsInvalidName(false);
}
if (e.key === "Enter") {
nameInputRef.current?.blur();
}
},
[currentFlow?.name],
[currentFlowName],
);
const handleNameSubmit = useCallback(() => {
if (
flowName.trim() !== "" &&
flowName !== currentFlow?.name &&
flowName !== currentFlowName &&
!isInvalidName
) {
// Get a one-time snapshot of currentFlow using get()
const currentFlowSnapshot = useFlowStore.getState().currentFlow;
const newFlow = {
...currentFlow!,
...currentFlowSnapshot!,
name: flowName,
id: currentFlow!.id,
id: currentFlowId!,
};
setCurrentFlow(newFlow);
saveFlow(newFlow)
@ -194,22 +213,23 @@ export const MenuBar = ({}: {}): JSX.Element => {
title: "Error updating flow name",
list: [(error as Error).message],
});
setFlowName(currentFlow?.name ?? "");
setFlowName(currentFlowName ?? "");
});
} else if (isInvalidName) {
setErrorData({
title: "Invalid flow name",
list: ["Name already exists"],
});
setFlowName(currentFlow?.name ?? "");
setFlowName(currentFlowName ?? "");
} else {
setFlowName(currentFlow?.name ?? "");
setFlowName(currentFlowName ?? "");
}
setEditingName(false);
setIsInvalidName(false);
}, [
flowName,
currentFlow,
currentFlowName,
currentFlowId,
setCurrentFlow,
saveFlow,
setSuccessData,
@ -218,10 +238,10 @@ export const MenuBar = ({}: {}): JSX.Element => {
]);
useEffect(() => {
if (currentFlow && !editingName) {
setFlowName(currentFlow.name);
if (currentFlowName && !editingName) {
setFlowName(currentFlowName);
}
}, [currentFlow, editingName]);
}, [currentFlowName, editingName]);
useEffect(() => {
if (measureRef.current) {
@ -230,12 +250,12 @@ export const MenuBar = ({}: {}): JSX.Element => {
}, [flowName]);
const swatchIndex =
(currentFlow?.gradient && !isNaN(parseInt(currentFlow?.gradient))
? parseInt(currentFlow?.gradient)
: getNumberFromString(currentFlow?.gradient ?? currentFlow?.id ?? "")) %
(currentFlowGradient && !isNaN(parseInt(currentFlowGradient))
? parseInt(currentFlowGradient)
: getNumberFromString(currentFlowGradient ?? currentFlowId ?? "")) %
swatchColors.length;
return currentFlow && onFlowPage ? (
return currentFlowName && onFlowPage ? (
<div
className="flex w-full items-center justify-center gap-2"
data-testid="menu_bar_wrapper"
@ -270,7 +290,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
</div>
<div className={cn(`flex rounded p-1`, swatchColors[swatchIndex])}>
<IconComponent
name={currentFlow?.icon ?? "Workflow"}
name={currentFlowIcon ?? "Workflow"}
className="h-3.5 w-3.5"
/>
</div>
@ -305,7 +325,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
onKeyDown={handleKeyDown}
onFocus={() => {
setEditingName(true);
setFlowName(currentFlow.name);
setFlowName(currentFlowName);
}}
onBlur={handleNameSubmit}
value={flowName}

View file

@ -0,0 +1,18 @@
import useFlowStore from "../stores/flowStore";
import useFlowsManagerStore from "../stores/flowsManagerStore";
import { customStringify } from "../utils/reactflowUtils";
export function useUnsavedChanges() {
const currentFlow = useFlowStore((state) => state.currentFlow);
const savedFlow = useFlowsManagerStore((state) => state.currentFlow);
if (!currentFlow || !savedFlow) {
return false;
}
if ((currentFlow?.data?.nodes?.length ?? 0) > 0) {
return false;
}
return customStringify(currentFlow) !== customStringify(savedFlow);
}

View file

@ -18,8 +18,12 @@ export default function FlowSettingsModal({
flowData,
details,
}: FlowSettingsPropsType): JSX.Element {
if (!open) return <></>;
const saveFlow = useSaveFlow();
const currentFlow = useFlowStore((state) => state.currentFlow);
const currentFlow = useFlowStore((state) =>
flowData ? undefined : state.currentFlow,
);
const setCurrentFlow = useFlowStore((state) => state.setCurrentFlow);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const flows = useFlowsManagerStore((state) => state.flows);