Skip to content

feat(jsx): add useAsync hook for managing asynchronous state and retries - #3743

Open
karthikj5453 wants to merge 4 commits into
Karanjot786:mainfrom
karthikj5453:feat/jsx-useAsync-hook
Open

feat(jsx): add useAsync hook for managing asynchronous state and retries#3743
karthikj5453 wants to merge 4 commits into
Karanjot786:mainfrom
karthikj5453:feat/jsx-useAsync-hook

Conversation

@karthikj5453

@karthikj5453 karthikj5453 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a dedicated useAsync hook to @termuijs/jsx to streamline asynchronous operations (data fetching, process execution, file I/O). The hook manages idle, loading, success, and error states, supports immediate execution, manual triggers (execute), state reset (reset), and includes unmount safety guards to prevent stale updates.

Which package(s)?

@termuijs/jsx

Type of Change

  • ✨ Feature (type:feature)

Checklist

  • ⭐ You starred the repo.
  • Tests pass locally: bun vitest run packages/jsx
  • Build passes: bun run build
  • Typecheck passes: bun run typecheck
  • You read CONTRIBUTING.md.
  • Your PR title follows type: short description.
  • No new any types without an inline comment.
  • No unrelated refactors bundled into this PR.

GSSoC 2026 Participation

Summary by CodeRabbit

  • New Features

    • Added a flexible useAsync hook with loading, success, error, idle, reset, refetch, and manual execution support.
    • Added configurable initial data, automatic execution, success and error callbacks, and typed async arguments.
    • Exposed async hook option and result types through the public JSX API.
  • Bug Fixes

    • Improved rating display for wide characters and truncated labels, preventing layout issues in constrained spaces.
  • Tests

    • Added comprehensive coverage for asynchronous states, callbacks, resets, refetching, and lifecycle behavior.

@github-actions github-actions Bot added type:feature +10 pts. New feature. type:docs +5 pts. Documentation. area:jsx @termuijs/jsx area:ui @termuijs/ui type:testing +10 pts. Tests. and removed type:feature +10 pts. New feature. labels Aug 12, 2026
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds a configurable useAsync hook with public types and tests. It updates Rating rendering to use display widths and refreshes contributor records.

Changes

Async hook

Layer / File(s) Summary
Async hook contract and exports
packages/jsx/src/hooks/useAsync.ts, packages/jsx/src/hooks.ts, packages/jsx/src/index.ts
Defines UseAsyncOptions, UseAsyncResult, and the backward-compatible AsyncState alias. Re-exports the hook and public types.
Async execution and lifecycle
packages/jsx/src/hooks/useAsync.ts
Adds immediate or deferred execution, state tracking, callbacks, reset, refetch, stale-result protection, and unmount safety.
Async hook validation
packages/jsx/src/hooks/useAsync.test.ts
Tests success, errors, callbacks, deferred execution, reset, typed arguments, stale-data clearing, and refetch().

Rating display width

Layer / File(s) Summary
Display-width-aware Rating rendering
packages/ui/src/Rating.ts
Uses stringWidth for glyph positioning and truncate for numerical label rendering.

Contributor records

Layer / File(s) Summary
Contributor record updates
CONTRIBUTORS.md
Updates one contribution count and replaces two contributor names.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Suggested reviewers: karanjot786

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the feature, package, change type, testing, and participation, but it omits the required Related Issue section and link. Add the required Related Issue section with the issue reference, such as Closes #123``, before merging.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding a JSX useAsync hook for asynchronous state and retries.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ 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.

@github-actions github-actions Bot added the type:feature +10 pts. New feature. label Aug 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/jsx/src/hooks/useAsync.test.ts (1)

39-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Settle the promise started by useAsync.

These tests call asyncFn, successFn, or failFn directly after the hook starts its own request. Each direct call creates a separate operation. The tests can miss duplicate execution and do not control the request that updates hook state.

Use a deferred promise from the first mock invocation. Resolve or reject that promise, flush microtasks, and assert the mock call count before rerendering.

Also applies to: 58-65, 82-90, 101-114, 120-126

🤖 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 `@packages/jsx/src/hooks/useAsync.test.ts` around lines 39 - 41, Update the
affected useAsync tests around their initial render and rerender flows to
control the request created by the hook: make the first asyncFn, successFn, or
failFn mock invocation return a deferred promise, settle it by resolving or
rejecting the deferred, flush microtasks, and assert the mock call count before
rerendering. Remove direct callback invocations that start separate operations,
preserving each test’s existing success and failure assertions.
🤖 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 `@packages/jsx/src/hooks/useAsync.ts`:
- Around line 92-107: Update the async execution flow in useAsync to call
setData(null) when loading begins and again when the request fails, ensuring
loading and error states do not retain successful data. Add a regression test
covering a successful execution followed by a failed refetch and verify data is
null after the failure.
- Line 35: Update useAsync and UseAsyncResult to accept a TArgs extends
unknown[] generic, replacing any[] in asyncFn and execute signatures with TArgs
so argument types are preserved and invalid execute calls are rejected. Replace
the referenced dependency-array any[] usages with unknown[], and thread TArgs
through all related declarations and returned functions.

In `@packages/ui/src/Rating.ts`:
- Around line 246-251: Update the glyph-rendering loop in Rating’s rendering
method to compute glyphWidth before writing, stop when glyphWidth is
non-positive or would exceed maxX, and advance currentX by glyphWidth after
rendering. Add regression tests covering a width-2 glyph at the final column and
a width-0 glyph followed by another glyph.

---

Nitpick comments:
In `@packages/jsx/src/hooks/useAsync.test.ts`:
- Around line 39-41: Update the affected useAsync tests around their initial
render and rerender flows to control the request created by the hook: make the
first asyncFn, successFn, or failFn mock invocation return a deferred promise,
settle it by resolving or rejecting the deferred, flush microtasks, and assert
the mock call count before rerendering. Remove direct callback invocations that
start separate operations, preserving each test’s existing success and failure
assertions.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b463875d-860a-4a12-876b-4a041cc7f767

📥 Commits

Reviewing files that changed from the base of the PR and between e4472c9 and d73ac97.

📒 Files selected for processing (6)
  • CONTRIBUTORS.md
  • packages/jsx/src/hooks.ts
  • packages/jsx/src/hooks/useAsync.test.ts
  • packages/jsx/src/hooks/useAsync.ts
  • packages/jsx/src/index.ts
  • packages/ui/src/Rating.ts

Comment thread packages/jsx/src/hooks/useAsync.ts Outdated
Comment thread packages/jsx/src/hooks/useAsync.ts
Comment thread packages/ui/src/Rating.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/jsx/src/hooks/useAsync.test.ts (2)

157-157: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Call refetch() in the failed-refetch test.

If this test covers the public refetch path, call res.refetch() on Line 175. The current call to res.execute() only tests direct execution. A regression in the refetch wrapper would not be detected here.

Proposed test adjustment
-        const failPromise = res.execute();
+        const failPromise = res.refetch();

Also applies to: 175-176

🤖 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 `@packages/jsx/src/hooks/useAsync.test.ts` at line 157, Update the
failed-refetch test “clears stale data during loading and error states on failed
refetch” to invoke the public res.refetch() method instead of res.execute() at
the existing retry call, ensuring the test covers the refetch wrapper path while
preserving its loading and error assertions.

182-190: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add an unmount-before-settlement test.

The test calls destroyFiber(fiber) only after failPromise settles on Line 182. This does not verify the hook's unmount guard. Add a deferred request, destroy the fiber before it settles, and assert that stale success or error callbacks do not run.

🤖 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 `@packages/jsx/src/hooks/useAsync.test.ts` around lines 182 - 190, Add an
unmount-before-settlement test near the existing useAsync failure coverage,
using a deferred request to keep the promise pending while the rendered hook is
active. Call destroyFiber(fiber) before resolving or rejecting the deferred
request, then settle it and assert that no stale success or error state
callbacks run after unmount.
🤖 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 `@packages/jsx/src/hooks/useAsync.test.ts`:
- Line 157: Update the failed-refetch test “clears stale data during loading and
error states on failed refetch” to invoke the public res.refetch() method
instead of res.execute() at the existing retry call, ensuring the test covers
the refetch wrapper path while preserving its loading and error assertions.
- Around line 182-190: Add an unmount-before-settlement test near the existing
useAsync failure coverage, using a deferred request to keep the promise pending
while the rendered hook is active. Call destroyFiber(fiber) before resolving or
rejecting the deferred request, then settle it and assert that no stale success
or error state callbacks run after unmount.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b8bc3a8-c584-4062-9239-fe583036a5b8

📥 Commits

Reviewing files that changed from the base of the PR and between d73ac97 and 1b85608.

📒 Files selected for processing (2)
  • packages/jsx/src/hooks/useAsync.test.ts
  • packages/jsx/src/hooks/useAsync.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/jsx/src/hooks/useAsync.ts

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

Labels

area:jsx @termuijs/jsx area:ui @termuijs/ui type:docs +5 pts. Documentation. type:feature +10 pts. New feature. type:testing +10 pts. Tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant