Isolate branch switcher query from WorkspaceScreenContent to prevent 20ms re-renders

Move useBranchSwitcher into BranchSwitcher component so query state
changes only re-render the small header widget instead of the entire
workspace screen. Also memo-wrap AgentStatusBar to skip re-renders
on keystroke.
This commit is contained in:
Mohamed Boudra
2026-04-12 12:38:46 +07:00
parent cde10485d4
commit acb294b9eb
4 changed files with 43 additions and 37 deletions
@@ -1,4 +1,4 @@
import { useCallback, useMemo, useRef, useState } from "react";
import { memo, useCallback, useMemo, useRef, useState } from "react";
import { View, Text, Platform, Pressable, Keyboard } from "react-native";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { useShallow } from "zustand/shallow";
@@ -848,7 +848,11 @@ function ControlledStatusBar({
const EMPTY_MODES: AgentMode[] = [];
export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStatusBarProps) {
export const AgentStatusBar = memo(function AgentStatusBar({
agentId,
serverId,
onDropdownClose,
}: AgentStatusBarProps) {
const { preferences, updatePreferences } = useFormPreferences();
const agent = useSessionStore(
useShallow((state) => {
@@ -1048,7 +1052,7 @@ export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStat
disabled={!client}
/>
);
}
});
export function DraftAgentStatusBar({
providerDefinitions,
+28 -11
View File
@@ -1,28 +1,45 @@
import { useRef } from "react";
import { Pressable, Text, View } from "react-native";
import { useQueryClient } from "@tanstack/react-query";
import { ChevronDown, GitBranch } from "lucide-react-native";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { Combobox, ComboboxItem, type ComboboxOption } from "@/components/ui/combobox";
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
import { useToast } from "@/contexts/toast-context";
import { useBranchSwitcher } from "@/hooks/use-branch-switcher";
interface BranchSwitcherProps {
currentBranchName: string | null;
title: string;
branchOptions: ComboboxOption[];
isOpen: boolean;
onOpenChange: (open: boolean) => void;
onBranchSelect: (branchId: string) => void;
serverId: string;
workspaceId: string;
isGitCheckout: boolean;
}
export function BranchSwitcher({
currentBranchName,
title,
branchOptions,
isOpen,
onOpenChange,
onBranchSelect,
serverId,
workspaceId,
isGitCheckout,
}: BranchSwitcherProps) {
const { theme } = useUnistyles();
const anchorRef = useRef<View>(null);
const client = useHostRuntimeClient(serverId);
const isConnected = useHostRuntimeIsConnected(serverId);
const toast = useToast();
const queryClient = useQueryClient();
const { branchOptions, isOpen, setIsOpen, handleBranchSelect } = useBranchSwitcher({
client,
normalizedServerId: serverId,
normalizedWorkspaceId: workspaceId,
currentBranchName,
isGitCheckout,
isConnected,
toast,
queryClient,
});
if (!currentBranchName) {
return (
@@ -36,7 +53,7 @@ export function BranchSwitcher({
<View ref={anchorRef} collapsable={false}>
<Pressable
testID="workspace-header-branch-switcher"
onPress={() => onOpenChange(true)}
onPress={() => setIsOpen(true)}
style={({ hovered, pressed }) => [
styles.branchSwitcherTrigger,
(hovered || pressed) && styles.branchSwitcherTriggerHovered,
@@ -53,14 +70,14 @@ export function BranchSwitcher({
<Combobox
options={branchOptions}
value={currentBranchName}
onSelect={onBranchSelect}
onSelect={handleBranchSelect}
searchable
placeholder="Switch branch..."
searchPlaceholder="Filter branches..."
emptyText="No branches found."
title="Switch branch"
open={isOpen}
onOpenChange={onOpenChange}
onOpenChange={setIsOpen}
anchorRef={anchorRef}
desktopPlacement="bottom-start"
desktopPreventInitialFlash