Skip to content

refactor: no derived state#1886

Open
mfortman11 wants to merge 1 commit into
mainfrom
no-derived-state
Open

refactor: no derived state#1886
mfortman11 wants to merge 1 commit into
mainfrom
no-derived-state

Conversation

@mfortman11

@mfortman11 mfortman11 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Every fix follows the same React-recommended technique: adjust state during render instead of in a useEffect. Compare the previous value of the upstream data; if it changed, call setState synchronously during render. React batches these updates and re-invokes the component before committing to the DOM, so the stale frame is never painted.

Summary by CodeRabbit

Release Notes

  • Refactor

    • Improved state synchronization mechanisms across the application for more responsive UI updates.
  • Bug Fixes

    • Fixed automatic expansion of function calls during streaming sessions.
    • Enhanced environment API key detection and switch synchronization.
    • Corrected auto-selection logic for models and providers based on current settings.
    • Improved search query input synchronization behavior.

@github-actions github-actions Bot added community frontend 🟨 Issues related to the UI/UX labels Jun 15, 2026
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Across 13 frontend files, useEffect-based state synchronization is replaced with a render-time conditional pattern: each component adds a prev* state variable, compares it to the current prop value during render, and calls the state setter immediately when a difference is detected. One exception is FunctionCallsContainer, which adds a new useEffect to auto-expand on streaming.

Changes

useEffect → Render-time prev-state Refactor

Layer / File(s) Summary
Onboarding component sync refactors
frontend/app/onboarding/_components/animated-provider-steps.tsx, frontend/app/onboarding/_components/anthropic-onboarding.tsx, frontend/app/onboarding/_components/model-selector.tsx, frontend/app/onboarding/_components/onboarding-card.tsx, frontend/app/onboarding/_hooks/useModelSelection.ts
AnimatedProviderSteps switches elapsed-time from state+effect to a derived constant and useMemo. AnthropicOnboarding tracks prevHasEnvApiKey to sync getFromEnv inline. ModelSelector tracks prevDefaultOpen to sync the popover open state inline. OnboardingCard tracks prevProviders to trigger provider auto-selection inline. useModelSelection tracks prevModelsData to set default model selections inline.
Chat, renderer, and navigation sync refactors
frontend/app/chat/_components/function-calls/container.tsx, frontend/components/chat-renderer.tsx, frontend/components/navigation.tsx
FunctionCallsContainer adds a useEffect to auto-expand the group when isStreaming is true. ChatRenderer adds prevSettingsStep and replaces the onboarding step sync effect with a render-time conditional. Navigation replaces previousConversationCount state with a useRef to track prior conversation count without triggering extra renders.
Knowledge search and filter context sync refactors
frontend/contexts/knowledge-filter-context.tsx, frontend/components/knowledge-search-bar.tsx, frontend/components/knowledge-search-input.tsx
KnowledgeFilterContext tracks prevSelectedFilter and clears queryOverride inline when the filter changes. KnowledgeSearchBar and KnowledgeSearchInput each add prevQueryOverride state and synchronize searchQueryInput inline, removing their respective useEffect blocks.
S3 settings and task dialog sync refactors
frontend/app/settings/_components/s3-settings-dialog.tsx, frontend/components/task-dialog/use-task-dialog.ts
S3SettingsDialog adds prevBucketNames and reconciles buckets/selectedBuckets inline via a comma-joined comparison. useTaskDialog moves the statusCategory zero-count reset from a useEffect to an inline render-time conditional.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • langflow-ai/openrag#1669: Modifies the same FunctionCallsContainer in container.tsx, specifically around isStreaming-driven expansion behavior.
  • langflow-ai/openrag#1700: Modifies the same useTaskDialog hook, overlapping with the statusCategory reset logic changed in this PR.

Suggested labels

refactor

Suggested reviewers

  • lucaseduoli
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: no derived state' directly reflects the primary objective of the PR—eliminating derived state patterns throughout the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch no-derived-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/app/onboarding/_components/anthropic-onboarding.tsx`:
- Around line 33-36: The synchronization block that updates state when
hasEnvApiKey changes is missing the cleanup logic that handleGetFromEnvChange
applies. Instead of directly calling setGetFromEnv(hasEnvApiKey), invoke
handleGetFromEnvChange(hasEnvApiKey) to ensure that when the environment API key
availability changes, any stale manual API keys or selected models are properly
cleaned up alongside the getFromEnv state change, preventing UI inconsistencies.

In `@frontend/app/onboarding/_hooks/useModelSelection.ts`:
- Around line 11-38: The default model selection in useModelSelection.ts (lines
11-38, the prevModelsData state and conditions checking languageModel and
embeddingModel) only runs when modelsData reference changes, missing cached data
on first render. Initialize prevModelsData to a sentinel value (like undefined
or a Symbol) instead of modelsData so the default selection logic runs
immediately when cached data is available. Additionally, in onboarding-card.tsx
(lines 88-129), initialize the auto-selection marker to a sentinel value and add
isEmbedding and isCloudBrand to the dependency array of any effect that tracks
these inputs, ensuring cached settings and context changes properly recompute
the provider selection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 32154f3a-9bf6-424a-a410-e0dd027f3ceb

📥 Commits

Reviewing files that changed from the base of the PR and between 6f982a9 and 5026949.

📒 Files selected for processing (13)
  • frontend/app/chat/_components/function-calls/container.tsx
  • frontend/app/onboarding/_components/animated-provider-steps.tsx
  • frontend/app/onboarding/_components/anthropic-onboarding.tsx
  • frontend/app/onboarding/_components/model-selector.tsx
  • frontend/app/onboarding/_components/onboarding-card.tsx
  • frontend/app/onboarding/_hooks/useModelSelection.ts
  • frontend/app/settings/_components/s3-settings-dialog.tsx
  • frontend/components/chat-renderer.tsx
  • frontend/components/knowledge-search-bar.tsx
  • frontend/components/knowledge-search-input.tsx
  • frontend/components/navigation.tsx
  • frontend/components/task-dialog/use-task-dialog.ts
  • frontend/contexts/knowledge-filter-context.tsx

Comment on lines +33 to +36
if (hasEnvApiKey !== prevHasEnvApiKey) {
setPrevHasEnvApiKey(hasEnvApiKey);
setGetFromEnv(hasEnvApiKey);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply the same cleanup when hasEnvApiKey changes.

This render-time sync flips getFromEnv without the cleanup in handleGetFromEnvChange, so a prop-driven switch can retain a stale manual key or selected models while the UI says it is using the environment key.

Proposed fix
-  if (hasEnvApiKey !== prevHasEnvApiKey) {
-    setPrevHasEnvApiKey(hasEnvApiKey);
-    setGetFromEnv(hasEnvApiKey);
-  }
   const debouncedApiKey = useDebouncedValue(apiKey, 500);
 
   // Fetch models from API when API key is provided
@@
     embeddingModels,
   } = useModelSelection(modelsData, isEmbedding);
+
+  if (hasEnvApiKey !== prevHasEnvApiKey) {
+    setPrevHasEnvApiKey(hasEnvApiKey);
+    setGetFromEnv(hasEnvApiKey);
+    if (hasEnvApiKey) {
+      setApiKey("");
+    }
+    setEmbeddingModel?.("");
+    setLanguageModel?.("");
+  }
 
   const handleGetFromEnvChange = (fromEnv: boolean) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/app/onboarding/_components/anthropic-onboarding.tsx` around lines 33
- 36, The synchronization block that updates state when hasEnvApiKey changes is
missing the cleanup logic that handleGetFromEnvChange applies. Instead of
directly calling setGetFromEnv(hasEnvApiKey), invoke
handleGetFromEnvChange(hasEnvApiKey) to ensure that when the environment API key
availability changes, any stale manual API keys or selected models are properly
cleaned up alongside the getFromEnv state change, preventing UI inconsistencies.

Comment on lines +11 to +38
// Set default selections when models first load (render-time adjustment)
const [prevModelsData, setPrevModelsData] = useState(modelsData);
if (modelsData !== prevModelsData) {
setPrevModelsData(modelsData);
if (modelsData) {
const defaultLangModel = isEmbedding
? undefined
: modelsData.language_models.find((m) => m.default);
const defaultEmbedModel = isEmbedding
? modelsData.embedding_models.find((m) => m.default)
: undefined;

// Set language model: prefer default, fallback to first available
if (!languageModel && !isEmbedding) {
const defaultLangModel = modelsData.language_models.find(
(m) => m.default,
);
if (defaultLangModel) {
setLanguageModel(defaultLangModel.value);
} else if (modelsData.language_models.length > 0) {
setLanguageModel(modelsData.language_models[0].value);
}
}

// Set embedding model: prefer default, fallback to first available
if (!embeddingModel && isEmbedding) {
const defaultEmbedModel = modelsData.embedding_models.find(
(m) => m.default,
);
if (defaultEmbedModel) {
setEmbeddingModel(defaultEmbedModel.value);
} else if (modelsData.embedding_models.length > 0) {
setEmbeddingModel(modelsData.embedding_models[0].value);
}
}
}
}, [modelsData, languageModel, embeddingModel, isEmbedding]);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The new prev-state guards skip initial cached React Query data. Initializing the “previous” value from the current upstream value means the render-time adjustment only runs after a later reference change, not when data is already available on first render.

  • frontend/app/onboarding/_hooks/useModelSelection.ts#L11-L38: guard default selection by “selection is empty and a default exists,” or initialize the previous-data marker to a sentinel so cached modelsData still selects defaults.
  • frontend/app/onboarding/_components/onboarding-card.tsx#L88-L129: initialize the auto-selection marker to a sentinel and include isEmbedding / isCloudBrand in the tracked inputs so cached settings and context changes recompute provider selection.
📍 Affects 2 files
  • frontend/app/onboarding/_hooks/useModelSelection.ts#L11-L38 (this comment)
  • frontend/app/onboarding/_components/onboarding-card.tsx#L88-L129
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/app/onboarding/_hooks/useModelSelection.ts` around lines 11 - 38,
The default model selection in useModelSelection.ts (lines 11-38, the
prevModelsData state and conditions checking languageModel and embeddingModel)
only runs when modelsData reference changes, missing cached data on first
render. Initialize prevModelsData to a sentinel value (like undefined or a
Symbol) instead of modelsData so the default selection logic runs immediately
when cached data is available. Additionally, in onboarding-card.tsx (lines
88-129), initialize the auto-selection marker to a sentinel value and add
isEmbedding and isCloudBrand to the dependency array of any effect that tracks
these inputs, ensuring cached settings and context changes properly recompute
the provider selection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community frontend 🟨 Issues related to the UI/UX refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant