Skip to content

fix(web): add requestIdleCallback fallback for Safari/iOS#9094

Open
bubacho wants to merge 1 commit into
makeplane:previewfrom
bubacho:fix/safari-request-idle-callback
Open

fix(web): add requestIdleCallback fallback for Safari/iOS#9094
bubacho wants to merge 1 commit into
makeplane:previewfrom
bubacho:fix/safari-request-idle-callback

Conversation

@bubacho
Copy link
Copy Markdown

@bubacho bubacho commented May 16, 2026

Summary

Fixes a runtime crash on Safari/iOS where window.requestIdleCallback is not available.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature
  • Breaking change
  • Documentation update

Problem

On iPhone Safari, opening a project issues page can crash with:
TypeError: window.requestIdleCallback is not a function.

The crash happens in RenderIfVisible and can break project/issue views.

Changes

  • Added a small runIdleTask helper:
    • uses window.requestIdleCallback when available
    • falls back to globalThis.setTimeout(..., 0) otherwise
  • Replaced direct window.requestIdleCallback calls with runIdleTask
  • Fixed browser check to typeof window !== "undefined"
  • Updated IntersectionObserver cleanup to unobserve a stable target reference
  • Removed children from the observer effect dependency list to avoid unnecessary observer recreation

Test scenarios

  1. Open project issues page in iPhone Safari/iOS -> no crash, page renders normally.
  2. Open the same page in Chrome/Firefox -> behavior unchanged.
  3. Trigger scrolling/visibility updates -> lazy render behavior still works.

Screenshots / media

N/A (runtime compatibility fix).

Why this is safe

  • No behavior change for browsers that support requestIdleCallback
  • Graceful fallback for unsupported browsers
  • Keeps lazy rendering logic intact while preventing runtime failures

Summary by CodeRabbit

  • Performance
    • Improved efficiency of visibility detection and rendering optimization for conditionally displayed content.

Review Change Stack

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 16, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

📝 Walkthrough

Walkthrough

This PR refactors render-if-visible-HOC.tsx to add a guarded runIdleTask helper and integrate it into the IntersectionObserver and visibility/height effects, ensuring scheduled callbacks are safe and observers correctly capture, observe, and unobserve their target elements with refined dependencies.

Changes

Idle task scheduling and observer fixes

Layer / File(s) Summary
Idle task scheduling utility
apps/web/core/components/core/render-if-visible-HOC.tsx
runIdleTask helper safely executes callbacks via window.requestIdleCallback when available (with timeout), otherwise uses setTimeout as fallback.
Observer fixes and idle-task integration
apps/web/core/components/core/render-if-visible-HOC.tsx
IntersectionObserver callback now uses runIdleTask on the useIdletime path with a window guard. Visibility effect captures the current target, observes it, and unobserves in cleanup; height-recording effect now schedules via runIdleTask and has narrowed dependencies.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 I hop where callbacks wait in line,
I tuck them into idle time,
With fallbacks soft when requests don't call,
Observers watch, then tidy all,
A little rabbit keeps the render fine.

🚥 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
Title check ✅ Passed The title accurately describes the main change: adding a requestIdleCallback fallback for Safari/iOS compatibility, which directly addresses the runtime crash fix in the pull request.
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.
Description check ✅ Passed The pull request description follows the required template structure with all key sections present: Summary, Type of Change, Problem, Changes, Test Scenarios, and References, providing sufficient detail about the runtime crash fix.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/web/core/components/core/render-if-visible-HOC.tsx (2)

26-32: ⚡ Quick win

Harden fallback path to avoid direct window usage.

Line 31 still assumes window exists. If this helper is reused in a non-browser context, it can throw. Prefer globalThis.setTimeout for a truly safe fallback.

Proposed change
 const runIdleTask = (callback: () => void) => {
   if (typeof window !== "undefined" && typeof window.requestIdleCallback === "function") {
     window.requestIdleCallback(callback, { timeout: 300 });
     return;
   }
-  window.setTimeout(callback, 0);
+  globalThis.setTimeout(callback, 0);
 };
🤖 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 `@apps/web/core/components/core/render-if-visible-HOC.tsx` around lines 26 -
32, The fallback in runIdleTask still calls window.setTimeout which can throw in
non-browser contexts; update the fallback to use globalThis.setTimeout (or guard
for typeof globalThis.setTimeout === "function") instead of window.setTimeout,
keeping the existing check for window.requestIdleCallback and leaving
requestIdleCallback usage unchanged; reference the runIdleTask function and the
window.requestIdleCallback branch when making this change.

79-79: ⚡ Quick win

Remove children from observer effect dependencies.

Line 79 unnecessarily ties observer lifecycle to children identity, which can recreate observers on normal rerenders. Keep dependencies to values that affect observer setup.

Proposed change
-  }, [intersectionRef, children, root, verticalOffset, horizontalOffset, useIdletime]);
+  }, [intersectionRef, root, verticalOffset, horizontalOffset, useIdletime]);
🤖 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 `@apps/web/core/components/core/render-if-visible-HOC.tsx` at line 79, The
effect that creates the IntersectionObserver in RenderIfVisibleHOC (the
useEffect that references intersectionRef, root, verticalOffset,
horizontalOffset, useIdletime) should not include `children` in its dependency
array because that causes unnecessary observer teardown/recreation on normal
rerenders; update the dependency array to remove `children` so it only depends
on stable values that affect observer setup (intersectionRef, root,
verticalOffset, horizontalOffset, useIdletime) and ensure any linter warnings
are addressed by keeping referenced variables stable or explicitly documenting
why `children` is excluded.
🤖 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.

Nitpick comments:
In `@apps/web/core/components/core/render-if-visible-HOC.tsx`:
- Around line 26-32: The fallback in runIdleTask still calls window.setTimeout
which can throw in non-browser contexts; update the fallback to use
globalThis.setTimeout (or guard for typeof globalThis.setTimeout === "function")
instead of window.setTimeout, keeping the existing check for
window.requestIdleCallback and leaving requestIdleCallback usage unchanged;
reference the runIdleTask function and the window.requestIdleCallback branch
when making this change.
- Line 79: The effect that creates the IntersectionObserver in
RenderIfVisibleHOC (the useEffect that references intersectionRef, root,
verticalOffset, horizontalOffset, useIdletime) should not include `children` in
its dependency array because that causes unnecessary observer
teardown/recreation on normal rerenders; update the dependency array to remove
`children` so it only depends on stable values that affect observer setup
(intersectionRef, root, verticalOffset, horizontalOffset, useIdletime) and
ensure any linter warnings are addressed by keeping referenced variables stable
or explicitly documenting why `children` is excluded.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16539ca9-e76f-4879-bca1-7bd08e2c45ad

📥 Commits

Reviewing files that changed from the base of the PR and between 50a7b47 and 62134f5.

📒 Files selected for processing (1)
  • apps/web/core/components/core/render-if-visible-HOC.tsx

@bubacho bubacho force-pushed the fix/safari-request-idle-callback branch from 62134f5 to d048ed2 Compare May 16, 2026 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants