Conversation
Removes observer() from components that don't access MobX observables directly: - error-display.tsx, error401-page.tsx, kowl-json-view.tsx - operation.tsx, require-auth.tsx - message-headers.tsx, message-meta-data.tsx, payload-component.tsx Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- router-sync: remove observer() wrapper - transforms-list: remove @observer class + observer(PartitionStatus), extract render to FC - group-list: remove @observer + autorun, extract render to FC with useUISettingsStore - admin-roles: remove @observer from AdminRoles class - admin-users: convert @observer class with @observable to functional component with useState - admin-debug-bundle-progress: remove @observer + unused @observable fields, extract render to FC - broker-details: remove @observer from classes + observer(DetailsDisplaySettings), extract to FC with useUISettingsStore - upload-license-page: remove @observer + useLocalObservable, convert to useState Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add missing Component import in connect/overview.tsx (TabClusters/TabTasks) - Add missing observer import in secrets-list.tsx (@observer decorator) - Fix typo coverifyTopicNotInList → verifyTopicNotInList in topic-list.spec.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…(61/94) Migrate 12 component files from MobX to standard React patterns: - rp-connect/pipelines-list: remove @observer + dummy @observable - rp-connect/secrets-list: remove @observer - rp-connect/secrets-create: extract functional component, @observable → useState, @computed → inline - rp-connect/secrets-update: extract functional component, @observable → useState - rp-connect/pipelines-details: extract functional component for isChangingPauseState; keep observer/observable/runInAction for streaming LogsTab - rp-connect/pipelines-create: extract functional component, 7 @observable fields → useState, secrets.updateWith() → derived from API - rp-connect/pipelines-edit: extract functional component, 9 @observable fields → useState initialized from pipeline prop, drop unused lintResults - connect/dynamic-ui/forms/error-wrapper: remove observer() wrapper - connect/dynamic-ui/forms/secret-input: useLocalObservable → useState + useRef - connect/dynamic-ui/forms/topic-input: useLocalObservable → useState with local controlled value - reassign-partitions/step3-review: remove @observer + dummy @observable - url-test-page: extract functional component, @observable → useState Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Migrated the final 9 component files to remove all MobX imports: - connector-details.tsx: observer/useLocalObservable/comparer/transaction → useState; LogsTab streaming via useRef/setInterval - group-details.tsx: @observer class → thin wrapper + GroupDetailsMain FC with useState - consumers/modals.tsx: @observer class → React class state; removed autorun/transaction - reassignment-tracker.ts: removed @observable/transaction; plain object returns - active-reassignments.tsx: @observer classes → React state; ThrottleDialog useLocalObservable → useState; @computed → inline - step1-partitions.tsx: removed @observer/@computed/transaction; selection mutations → onPartitionSelectionChange callback - step2-brokers.tsx: removed transaction/observer(); broker mutations → onSelectionChange callback - reassign-partitions.tsx: 10 @observable fields → React state; autorun → componentDidUpdate; @computed → plain getters - misc/kowl-table.tsx: removed @observer; filterOpen/quickSearch → local React state src/components/ is now 100% MobX-free (94/94 files). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… utils) - modal-container: replace observable([]) with plain array + stored setState dispatcher - tsx-utils: TimestampDisplay class → function with useEffect/setInterval for relative time; StatusIndicator @observable showWaitingText → React class state - utils: remove @observable/@makeObservable from DebugTimerStore; drop useSeconds/useFrame methods Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All three root components (App, EmbeddedApp, ConsoleApp) don't read any MobX observables directly — the observer() wrappers were vestigial. MobX is now fully removed from all non-state source files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- tsx-utils: restore missing useEffect import (stripped by linter) - pipelines-details: add missing useRef, useEffect, SortingState imports - principal-group-editor: cast setState updater result as AclPrincipalGroup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@claude review mobx updates |
|
Claude finished @malinskibeniamin's task in 4m 54s —— View job MobX Refactor Review
|
Pages extend PageComponent and read api.* (MobX observable) in render. Without observer(), async data arrivals didn't trigger re-renders. Fix: wrap each PageComponent subclass's render in a MobX Reaction in the constructor. The reaction tracks api.* accesses during render and calls forceUpdate() when observed values change (e.g. when fetched data arrives). Also add super.componentWillUnmount() to ReassignPartitions so its reaction is disposed on unmount. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pages that delegate to functional child components need to explicitly touch the api.* observables in their render() method so the PageComponent Reaction tracks them and triggers re-renders when async data arrives. Fixes topic configuration page (TopicDetails → TopicDetailsContent), consumer group details (GroupDetails → GroupDetailsMain), and connector details (KafkaConnectorDetails → KafkaConnectorMain). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e updates Direct .push()/.remove() on the entries array didn't trigger re-renders because the Proxy in CreateTopicModal only intercepts top-level property assignments. Convert to immutable updates via onChange callback so state.additionalConfig = newArray flows through the Proxy and calls forceUpdate(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| ); | ||
| } | ||
|
|
||
| const SidebarNavigation = observer(() => { |
There was a problem hiding this comment.
Let's double check if that still works when in embedded mode with cloud UI, sidebar items need to refresh
| ? p.operation | ||
| : Object.keys(AclOperation).find((key) => AclOperation[key as keyof typeof AclOperation] === p.operation) || | ||
| 'Unknown'; | ||
| const operationName = |
There was a problem hiding this comment.
I think this deserves its own utility function
| options={[ | ||
| { | ||
| value: 'Any', | ||
| label: <OptionContent icon={icons.minus}>Not set</OptionContent>, |
| await rolesApi.refreshRoleMembers(); | ||
| } | ||
|
|
||
| class RoleDetailsPage extends PageComponent<{ roleName: string }> { |
There was a problem hiding this comment.
I guess functional components can be used later on?
| // or an array of connectors (in which case it will show the sum) | ||
|
|
||
| export const TasksColumn = observer((props: { observable: ClusterConnectors | ClusterConnectorInfo }) => { | ||
| export const TasksColumn = (props: { observable: ClusterConnectors | ClusterConnectorInfo }) => { |
There was a problem hiding this comment.
Do we still need to keep the observable if we removed the observer pattern?
There was a problem hiding this comment.
it's just a variable name, I see it's not optimal, but I think this can be addressed later on, I want to make as few changes as possible.
| </span> | ||
| ); | ||
| let icon: JSX.Element = <></>; | ||
| if (state === ConnectorState.Running) { |
There was a problem hiding this comment.
I think it should be its own component or utility method
- Replace all MobX observables/computeds in backend-api.ts with Zustand stores + Proxy exports; zero changes to 92 consumer files - Update PageComponent to subscribe to Zustand stores instead of using MobX Reaction for reactivity - Remove runInAction from topic-settings-store.ts - Remove mobx and mobx-react packages (bun remove) - Fix E2E quota tests: delete stale quota-page.js/topic-page.js that shadowed the .ts sources and were missing clickNextPage/clickPreviousPage - Fix use-list-quotas.ts: use pageSize 1000 instead of MAX_PAGE_SIZE=25 so all quotas are available for client-side pagination - Fix topic-messages-timestamp.spec.ts: scope input[type=time] to page (not start-timestamp-input, since time input is in Chakra portal); set up waitForRequest before fill() that triggers the search; wait for popover close animation before verifying display input value; use input[readonly] locator to avoid ARIA textbox role ambiguity Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| * navigates internally, allowing the shell to sync its router state. | ||
| */ | ||
| export const RouterSync = observer(() => { | ||
| export const RouterSync = () => { |
There was a problem hiding this comment.
Let's double check embedded mode functionality with cloud UI route syncing
|
@claude review extensively the state management logic. We need to make sure business logic remain intact. Look into any potential side effects and double check all observable patterns are properly substituted to ensure no regressions. Also investigate Kafka connect/reassign partitions features to endure they continue to be operational. |
|
Claude finished @malinskibeniamin's task in 7m 8s —— View job Deep State Management Review
|
| Area | Status |
|---|---|
| Kafka Connect — Form/JSON toggle | 🔴 Broken |
| Kafka Connect — Property inputs | 🔴 Broken (controlled inputs frozen) |
| Kafka Connect — Loading skeleton | 🔴 Non-deterministic/stuck |
| Kafka Connect — Error cycling | 🟡 Non-functional click |
| Reassign Partitions — Core flow | 🟢 Works via PageComponent subscription |
| Reassign Partitions — Tracker timing | 🟡 Slightly indirect but functional |
| Error Modal | 🟢 Correct |
| Router sync / sidebar | 🟢 Correct (proper hooks) |
The Kafka Connect configuration UI has several interconnected reactivity gaps stemming from ConnectorPropertiesStore being a plain class whose mutations no React component subscribes to. Before merging, the Form/JSON switch, Show Advanced Options, and all controlled property inputs should be validated end-to-end in a browser. The fastest fix is to lift viewMode, showAdvancedOptions, and initPending transitions into React state (or add a useEffect that subscribes to connectorStore.subscribe() and calls a local state setter to trigger re-renders).
| "mobx": "^6.10.0", | ||
| "mobx-react": "^7.6.0", |
There was a problem hiding this comment.
big moment if we can finally get there. thank you
There was a problem hiding this comment.
also regenerate yarn.lock
There was a problem hiding this comment.
yarn is not installed in this environment — can you regenerate yarn.lock locally with yarn install after merging bun.lock?
There was a problem hiding this comment.
You can do "bun install --yarn"
| updateSidebarItems(); | ||
| useApiStore.subscribe((state, prev) => { | ||
| if (state.endpointCompatibility !== prev.endpointCompatibility) { | ||
| updateSidebarItems(); | ||
| } | ||
| }); |
There was a problem hiding this comment.
would this be sufficient? this would mean sidebar items get updated twice, I just want to make sure no race conditions appear.
There was a problem hiding this comment.
Good question — no race condition here. The two calls are intentional: the first fires immediately so the sidebar is populated on startup (in case endpointCompatibility is already set); the second fires when it changes (e.g., after the initial API fetch populates it from null). Since updateSidebarItems() reads the current store snapshot and calls setSidebarItems() synchronously, calling it twice is harmless — the second just overwrites with the same or more accurate result.
| import { MAX_PAGE_SIZE } from '../react-query/react-query.utils'; | ||
|
|
||
| const request = create(ListQuotasRequestSchema, { pageSize: MAX_PAGE_SIZE }); | ||
| const request = create(ListQuotasRequestSchema, { pageSize: 1000 }); |
There was a problem hiding this comment.
why not keep using MAX_PAGE_SIZE?
| const [tasks, setTasks] = useState(cpuToTasks(pipeline?.resources?.cpuShares) || MIN_TASKS); | ||
| const [editorContent, setEditorContent] = useState(pipeline.configYaml); | ||
| const [isUpdating, setIsUpdating] = useState(false); | ||
| const [tags] = useState(pipeline.tags); |
There was a problem hiding this comment.
Do we need useState hook if we are never changing the state for the tags? I think they will always remain the same based on this code?
| toast({ | ||
| status: 'success', | ||
| duration: 4000, | ||
| status: 'warning', |
There was a problem hiding this comment.
should it be info instead? resizing pipeline should not be a warning, it's just a message
There was a problem hiding this comment.
nevermind, that was the functionality before, fine with either
…fo toast - use-list-quotas: restore MAX_PAGE_SIZE instead of hardcoded 1000 - pipelines-edit: replace useState for tags with plain const (state never changes) - pipelines-edit: change pipeline resize toast status from warning to info Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| if (isServerless()) { | ||
| this.topicTabs.removeAll((x) => x.id === 'documentation'); | ||
| } |
There was a problem hiding this comment.
Does this logic still exist anywhere? Do we still need it?
There was a problem hiding this comment.
Yes, it's still needed — api.topicPartitions.get(topic.topicName) is read in tab-partitions.tsx. Removed the stale comment about 'statistics bar' and updated it to reflect the actual usage.
| // Touch observables so PageComponent's Reaction tracks them and triggers re-renders | ||
| // when async data arrives (TopicDetailsContent is a functional component child | ||
| // that reads api.* outside the MobX tracking context). | ||
| void api.topics; | ||
| void api.topicConfig.get(this.props.topicName); |
There was a problem hiding this comment.
not sure if still relevant given mobx is fully cleaned up
There was a problem hiding this comment.
You're right — removed. PageComponent now re-renders via Zustand store subscriptions (useApiStore.subscribe(forceUpdate)), so the MobX-style observable touches are no longer needed.
|
|
||
| return partitions; | ||
| }); | ||
| })(); |
There was a problem hiding this comment.
nit: having anonymous arrow function called makes it hard to read but it's fine for now
The `void api.topics` and `void api.topicConfig.get()` lines were MobX-specific patterns to register observable access in a reaction. PageComponent now re-renders via Zustand store subscriptions, so these are no longer needed. Also fix outdated comment on refreshPartitionsForTopic (it feeds the Partitions tab, not the stats bar). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add onSuccess callback to ConfirmModal that fires after the success toast is queued. Use it in the delete connector flow instead of navigating inside onOk — navigating inside onOk caused refreshData to clear the connector from the store, unmounting the component tree before the toast rendered. Update the e2e test to assert on navigation + connector absence rather than the ephemeral toast, which is inherently flaky. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
f4e5842 to
3dd44b6
Compare
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
876c0b7 to
229e240
Compare
| api.refreshDebugBundleStatuses().catch(() => { | ||
| // Error handling managed by API layer | ||
| }); | ||
| }, 2000); |
There was a problem hiding this comment.
In the future we could replace it by using tanstack query polling mode

Removes observer() from components that don't access MobX observables directly: