Skip to content

Conversation

@saseungmin
Copy link
Owner

@saseungmin saseungmin commented Aug 8, 2025

  • Remove redundant currentIndex state to avoid duplicate callbacks
  • Use manager subscription as single source of truth for index changes
  • Implement ref pattern for onIndexChange to prevent stale closures
  • Ensure onIndexChange only fires on actual user interactions, not internal state changes

Now onIndexChange correctly fires only once during initialization.

Fixes #67

Summary by CodeRabbit

  • Bug Fixes

    • Resolved an issue where the onIndexChange callback was triggered multiple times during initialization. It now fires exactly once, ensuring accurate event handling for index changes.
  • Refactor

    • Improved internal logic to prevent duplicate callback invocations and ensure onIndexChange is only called on genuine user interactions.

- Remove redundant currentIndex state to avoid duplicate callbacks
- Use manager subscription as single source of truth for index changes
- Implement ref pattern for `onIndexChange` to prevent stale closures
- Ensure `onIndexChange` only fires on actual user interactions, not internal state changes

Now `onIndexChange` correctly fires only once during initialization.

Fixes #67
@changeset-bot
Copy link

changeset-bot bot commented Aug 8, 2025

🦋 Changeset detected

Latest commit: 8eb8395

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Aug 8, 2025

Walkthrough

This update modifies the useGestureViewer hook to resolve duplicate invocations of the onIndexChange callback during initialization. It removes local state tracking for the current index, introduces a ref for the callback to prevent stale closures, and ensures onIndexChange is triggered only on genuine user interactions, not internal state changes.

Changes

Cohort / File(s) Change Summary
Hook Logic Refactor
src/useGestureViewer.ts
Removed internal currentIndex state and its setter. Introduced a ref for onIndexChange, updated via effect. Callback is now invoked directly from the manager subscription, ensuring correct timing and eliminating duplicate calls. Exported currentIndex was removed from the hook's return value.
Documentation/Changelog
.changeset/major-areas-mix.md
Documents the rationale and summary of the changes, including the fix for duplicate callback invocations and the new callback ref pattern.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant GestureViewer
    participant useGestureViewer
    participant GestureViewerManager

    User->>GestureViewer: Mount with initialIndex > 0
    GestureViewer->>useGestureViewer: Initialize hook
    useGestureViewer->>GestureViewerManager: Subscribe to index changes
    GestureViewerManager-->>useGestureViewer: Notify index (once)
    useGestureViewer-->>GestureViewer: Call onIndexChange(index)
    Note over useGestureViewer: No duplicate calls<br>on initialization
    User->>GestureViewer: Swipe to change image
    GestureViewer->>useGestureViewer: Handle user interaction
    useGestureViewer->>GestureViewerManager: Update index
    GestureViewerManager-->>useGestureViewer: Notify index change
    useGestureViewer-->>GestureViewer: Call onIndexChange(new index)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Assessment against linked issues

Objective Addressed Explanation
Ensure onIndexChange is called only once during initialization, not multiple times (#67)
Ensure onIndexChange is only called on actual user interactions, not internal state changes (#67)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Possibly related PRs

Poem

A bunny hopped through gesture code,
Where callbacks once would overflowed.
Now, with a single, timely cheer,
Index changes are crystal clear.
No more echoes, just one call—
This patch brings peace to one and all!
🐇✨

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6883dd7 and 8eb8395.

📒 Files selected for processing (1)
  • src/useGestureViewer.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: saseungmin
PR: saseungmin/react-native-gesture-image-viewer#4
File: src/useGestureImageViewer.ts:184-207
Timestamp: 2025-07-02T17:37:07.501Z
Learning: In React Native Gesture Handler, the pinch gesture event.scale always starts from 1.0 at the beginning of each new pinch gesture and is relative/multiplicative, not absolute. To maintain smooth scaling, you need to multiply the current scale by event.scale rather than directly assigning it.
📚 Learning: 2025-07-02T17:37:07.501Z
Learnt from: saseungmin
PR: saseungmin/react-native-gesture-image-viewer#4
File: src/useGestureImageViewer.ts:184-207
Timestamp: 2025-07-02T17:37:07.501Z
Learning: In React Native Gesture Handler, the pinch gesture event.scale always starts from 1.0 at the beginning of each new pinch gesture and is relative/multiplicative, not absolute. To maintain smooth scaling, you need to multiply the current scale by event.scale rather than directly assigning it.

Applied to files:

  • src/useGestureViewer.ts
🔇 Additional comments (1)
src/useGestureViewer.ts (1)

58-61: LGTM! Proper ref pattern implementation.

The new refs correctly implement the solution for preventing duplicate onIndexChange calls. The onIndexChangeRef prevents stale closures and lastEmittedIndexRef tracks emissions to avoid duplicates.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/prevent-multiple-onindexchange-calls-during-init

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@saseungmin saseungmin self-assigned this Aug 8, 2025
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Aug 8, 2025

Deploying react-native-gesture-image-viewer with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8eb8395
Status: ✅  Deploy successful!
Preview URL: https://a2d6d15c.react-native-gesture-image-viewer.pages.dev
Branch Preview URL: https://fix-prevent-multiple-onindex.react-native-gesture-image-viewer.pages.dev

View logs

Copy link

@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.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/useGestureViewer.ts (2)

58-58: Prefer a precise ref type over any for listRef

Using any loses type-safety and IntelliSense. If this is a FlatList, type it accordingly.

-  const listRef = useRef<any>(null);
+  const listRef = useRef<FlatList<any> | null>(null);

Add the import (outside this range):

import { FlatList } from 'react-native';

60-60: Initialize the callback ref with the prop to avoid a pre-effect window

Tiny improvement: seed the ref with the initial prop so the first manager emission (if any) won’t hit null before the effect runs.

-  const onIndexChangeRef = useRef<((index: number) => void) | null>(null);
+  const onIndexChangeRef = useRef<((index: number) => void) | null>(onIndexChange ?? null);
+  const lastEmittedIndexRef = useRef<number | null>(null);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4721d9f and 6883dd7.

📒 Files selected for processing (2)
  • .changeset/major-areas-mix.md (1 hunks)
  • src/useGestureViewer.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: saseungmin
PR: saseungmin/react-native-gesture-image-viewer#4
File: src/useGestureImageViewer.ts:184-207
Timestamp: 2025-07-02T17:37:07.501Z
Learning: In React Native Gesture Handler, the pinch gesture event.scale always starts from 1.0 at the beginning of each new pinch gesture and is relative/multiplicative, not absolute. To maintain smooth scaling, you need to multiply the current scale by event.scale rather than directly assigning it.
📚 Learning: 2025-07-02T17:37:07.501Z
Learnt from: saseungmin
PR: saseungmin/react-native-gesture-image-viewer#4
File: src/useGestureImageViewer.ts:184-207
Timestamp: 2025-07-02T17:37:07.501Z
Learning: In React Native Gesture Handler, the pinch gesture event.scale always starts from 1.0 at the beginning of each new pinch gesture and is relative/multiplicative, not absolute. To maintain smooth scaling, you need to multiply the current scale by event.scale rather than directly assigning it.

Applied to files:

  • .changeset/major-areas-mix.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (2)
.changeset/major-areas-mix.md (2)

7-12: Call out API surface changes and guard against duplicate emissions

The note says there are no public API changes, but the hook appears to no longer return currentIndex. If accurate, highlight this in the changelog. Also, to guarantee “only on actual index changes,” guard emissions in the subscription as suggested in code review.

I can help update the Changeset text once we confirm whether the return shape changed.

Likely an incorrect or invalid review comment.


2-2: No breaking change detected; patch bump is appropriate

After searching the codebase, the useGestureViewer hook has never returned a currentIndex value, and the useGestureViewerController hook continues to expose currentIndex. There is no removal of that export, so this release does not introduce a breaking change and the patch-level version bump is correct.

@saseungmin saseungmin merged commit a57ddbd into main Aug 8, 2025
6 checks passed
@saseungmin saseungmin deleted the fix/prevent-multiple-onindexchange-calls-during-init branch August 8, 2025 02:16
saseungmin added a commit that referenced this pull request Aug 8, 2025
- Remove redundant currentIndex state to avoid duplicate callbacks
- Use manager subscription as single source of truth for index changes
- Implement ref pattern for `onIndexChange` to prevent stale closures
- Ensure `onIndexChange` only fires on actual user interactions, not internal state changes

Now `onIndexChange` correctly fires only once during initialization.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: onIndexChange called multiple times during initialisation when initialIndex > 0

2 participants