Skip to content

Use lazy state initialization for complex initial objects in tool UIs #1009

@MODSetter

Description

@MODSetter

Description

In the Linear, Confluence, and Jira tool UI components, a complex initialEditState object (with conditional logic and .map() calls) is computed as a regular variable and passed to useState(). React only uses the initial value on the first render, but the computation runs on every render because it's not wrapped in a lazy initializer function.

Vercel React Best Practices Rule: rerender-lazy-state-init (5.10)

Files to change

  • surfsense_web/components/tool-ui/linear/update-linear-issue.tsx
  • surfsense_web/components/tool-ui/confluence/update-confluence-page.tsx
  • surfsense_web/components/tool-ui/jira/update-jira-issue.tsx
  • surfsense_web/components/settings/llm-role-manager.tsx

What to do

In update-linear-issue.tsx (lines 185-208):

Move the initialEditState object construction inside a lazy initializer:

// Before
const initialEditState = {
  title: actionArgs.new_title ? String(actionArgs.new_title) : (issue?.title ?? args.new_title ?? ""),
  // ... more fields
  labelIds: Array.isArray(actionArgs.new_label_ids)
    ? (actionArgs.new_label_ids as string[])
    : (issue?.current_labels?.map((l) => l.id) ?? []),
};
const [editedArgs, setEditedArgs] = useState(initialEditState);

// After
const [editedArgs, setEditedArgs] = useState(() => ({
  title: actionArgs.new_title ? String(actionArgs.new_title) : (issue?.title ?? args.new_title ?? ""),
  description: actionArgs.new_description ? String(actionArgs.new_description) : (issue?.description ?? args.new_description ?? ""),
  stateId: actionArgs.new_state_id ? String(actionArgs.new_state_id) : (issue?.current_state?.id ?? "__none__"),
  assigneeId: actionArgs.new_assignee_id ? String(actionArgs.new_assignee_id) : (issue?.current_assignee?.id ?? "__none__"),
  priority: actionArgs.new_priority != null ? String(actionArgs.new_priority) : String(issue?.priority ?? 0),
  labelIds: Array.isArray(actionArgs.new_label_ids) ? (actionArgs.new_label_ids as string[]) : (issue?.current_labels?.map((l) => l.id) ?? []),
}));

Delete the now-unused initialEditState variable.

Apply the same pattern in:

  • update-confluence-page.tsx (lines 152-162)
  • update-jira-issue.tsx (lines 176-189)

In llm-role-manager.tsx (lines 115-119):

// Before
const [assignments, setAssignments] = useState({
  agent_llm_id: preferences.agent_llm_id ?? "",
  document_summary_llm_id: preferences.document_summary_llm_id ?? "",
  image_generation_config_id: preferences.image_generation_config_id ?? "",
});

// After
const [assignments, setAssignments] = useState(() => ({
  agent_llm_id: preferences.agent_llm_id ?? "",
  document_summary_llm_id: preferences.document_summary_llm_id ?? "",
  image_generation_config_id: preferences.image_generation_config_id ?? "",
}));

Acceptance criteria

  • All four files use useState(() => ({ ... })) instead of useState({ ... })
  • Tool UIs and LLM role assignment still initialize correctly
  • The initialEditState variable no longer exists as a separate declaration in the tool UI files

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions