OCPBUGS-98449: Replace global Redux timestamp dispatch with local useSyncExternalStore hook#16746
OCPBUGS-98449: Replace global Redux timestamp dispatch with local useSyncExternalStore hook#16746alimobrem wants to merge 1 commit into
Conversation
…re hook
Problem:
Every 10 seconds, app.tsx dispatched UIActions.updateTimestamps(Date.now())
to the Redux store. This caused all ~36 Timestamp components to re-render
simultaneously via useConsoleSelector. Worse, the Redux dispatch triggered
selector comparisons in every component subscribed to the UI store slice —
not just Timestamps — adding unnecessary work across the entire app on
every tick. The Immutable.js state update (state.set('lastTick', ...)) also
added serialize/deserialize overhead on every dispatch.
Solution:
Replace with a self-contained useTimestampTick hook using React 18's
useSyncExternalStore. A single shared setInterval starts when the first
Timestamp mounts and stops when the last unmounts (no wasted cycles when
no Timestamps are visible). The cached tick value ensures getSnapshot
returns a stable reference between notifications, preventing spurious
re-renders. The Redux store is no longer involved in timestamp updates.
Changes:
- New hook: useTimestampTick (useSyncExternalStore + shared interval)
- Timestamp.tsx: swap useConsoleSelector for useTimestampTick
- app.tsx: remove global setInterval dispatch
- Remove dead Redux code: UpdateTimestamps enum, updateTimestamps action
creator, lastTick reducer case (no remaining consumers confirmed via
codebase-wide grep)
SDK compatibility:
The Timestamp component is exported in the dynamic plugin SDK
(core-api.ts:726). TimestampProps is unchanged — external plugins pass a
timestamp string and get rendered output. The tick mechanism is internal.
updateTimestamps was never part of the public SDK API.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Pipeline controller notification For optional jobs, comment This repository is configured in: LGTM mode |
Walkthrough
ChangesTimestamp tick migration
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 14 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (14 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
/retest ci/prow/backend |
|
/test backend |
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: alimobrem, spadgett The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
Scheduling tests matching the |
|
@alimobrem: This pull request references Jira Issue OCPBUGS-98449, which is invalid:
Comment The bug has been updated to refer to the pull request using the external bug tracker. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/jira refresh |
|
@alimobrem: This pull request references Jira Issue OCPBUGS-98449, which is invalid:
Comment DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/jira refresh |
|
@alimobrem: This pull request references Jira Issue OCPBUGS-98449, which is valid. 3 validation(s) were run on this bug
DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/verified by @spadgett |
|
@spadgett: This PR has been marked as verified by DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/test e2e-playwright |
|
/label docs-approved |
|
/retest-required |
|
/test e2e-playwright |
|
@alimobrem: The following test failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
| const formattedDate = dateTimeFormatter().format(new Date(timestamp)); | ||
| expect(screen.getByText(formattedDate)).toBeDefined(); | ||
| }); | ||
| it('should show dash for null timestamp', () => { |
There was a problem hiding this comment.
| it('should show dash for null timestamp', () => { | |
| it('should show dash when timestamp is undefined', () => { |
Summary
setIntervalinapp.tsxthat dispatchedUIActions.updateTimestamps(Date.now())every 10s to the Redux store, causing all ~36 Timestamp components plus every other UI store subscriber to re-render simultaneouslyuseTimestampTickhook using React 18'suseSyncExternalStorewith a shared interval that starts on first Timestamp mount and stops on last unmountUpdateTimestampsenum member,updateTimestampsaction creator, andlastTickreducer case (no remaining consumers confirmed via codebase-wide grep)Timestampcomponent props unchanged,updateTimestampswas never part of the public SDK APIDetails
The old approach dispatched a Redux action every 10 seconds, which triggered:
state.set('lastTick', ...)in the UI reducerThe new
useTimestampTickhook:useSyncExternalStorewith a cachedtickvalue that only changes when the interval fires (stablegetSnapshot)setIntervalacross all consumers via aSet<listener>patternnumber(epoch ms) directly, avoiding the Immutable.js serialization roundtripTest plan
useTimestampTickhook tests passing (5 tests covering: value type, tick update, no early update, cleanup, shared interval)🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests