Skip to content

Conversation

@debangshu919
Copy link
Contributor

Solution

Added a dependency array [target_ids] to the second useEffect to ensure:

  • The effect re-runs when target IDs change.
  • Event listeners are properly cleaned up and re-attached.
  • For dynamic elements, a MutationObserver watches for DOM changes and attaches listeners once targets are available.

Changes Made

  1. Added [target_ids] dependency to the listener attachment useEffect.
  2. Implemented MutationObserver to handle elements that don't exist at initial render.
  3. Proper cleanup of both observers and event listeners on unmount.

All Submissions

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)

Changes To Core Features

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Screenshots

image

closes #6032

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 12, 2025

Greptile Overview

Greptile Summary

This PR fixes a bug where the rx.clipboard paste event handler failed to bind on first paste when components were rendered dynamically inside rx.dialog (via rx.foreach or rx.match). The fix adds proper dependency tracking and DOM observation for dynamic elements.

Key Changes:

  • Added [target_ids] dependency array to the listener-attachment useEffect so it re-runs when target IDs change
  • Implemented refs pattern (useRef + separate useEffect) to prevent stale closures in event handlers
  • Added MutationObserver to watch for DOM changes and attach listeners once dynamic target elements become available
  • Proper cleanup of both observers and event listeners on unmount or when dependencies change

How It Works:
The hook now attempts to attach listeners immediately. If target elements don't exist yet (dynamic rendering), a MutationObserver watches the DOM and attaches listeners once all targets appear. The observer automatically disconnects after successful attachment. Event handlers use refs to always access the latest callbacks, preventing stale closure issues.

Testing:
The fix directly addresses the reproduction case in issue #6032, where paste events in dynamically rendered dialogs required typing before working. With this change, paste handlers bind immediately when elements appear in the DOM.

Confidence Score: 4/5

  • This PR is safe to merge with minimal risk
  • The implementation is solid, using proper React patterns (refs for stable closures, dependency arrays, cleanup functions). The MutationObserver correctly handles dynamic elements and disconnects after successful attachment. Score is 4 instead of 5 because no automated tests were added for this complex dynamic rendering scenario, making future regression possible
  • No files require special attention - the implementation is sound

Important Files Changed

File Analysis

Filename Score Overview
reflex/.templates/web/utils/helpers/paste.js 4/5 Fixed paste handler binding by adding dependency array and MutationObserver for dynamic elements; uses refs to prevent stale closures

Sequence Diagram

sequenceDiagram
    participant React
    participant usePasteHandler
    participant DOM
    participant MutationObserver
    participant EventListener
    
    Note over React,usePasteHandler: Component renders with clipboard handler
    
    React->>usePasteHandler: Call with target_ids, event_actions, on_paste
    usePasteHandler->>usePasteHandler: Store on_paste & event_actions in refs
    
    alt target_ids dependency changes
        usePasteHandler->>usePasteHandler: Cleanup old listeners & observer
        usePasteHandler->>DOM: Query for target elements by ID
        
        alt All target elements exist
            usePasteHandler->>EventListener: Attach paste listeners to targets
            Note over usePasteHandler: Setup complete
        else Some/all target elements missing (dynamic render)
            usePasteHandler->>MutationObserver: Create observer for DOM changes
            MutationObserver->>DOM: Watch document.body for childList changes
            
            loop Until all targets found
                DOM->>MutationObserver: DOM mutation detected
                MutationObserver->>DOM: Query for target elements
                
                alt Still missing targets
                    Note over MutationObserver: Continue observing
                else All targets now exist
                    MutationObserver->>EventListener: Attach paste listeners
                    MutationObserver->>MutationObserver: Disconnect observer
                end
            end
        end
    end
    
    Note over EventListener: User pastes content
    EventListener->>usePasteHandler: Paste event fired
    usePasteHandler->>usePasteHandler: Use current refs (latest handlers)
    usePasteHandler->>React: Call on_paste with clipboard data
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (1)

  1. reflex/.templates/web/utils/helpers/paste.js, line 85-90 (link)

    logic: the MutationObserver callback can fire many times before elements appear, potentially creating duplicate listeners if tryAttach() succeeds while previous listeners aren't cleaned up

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

masenf
masenf previously approved these changes Dec 12, 2025
Copy link
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

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

this is an excellent PR for a very annoying issue that's been around since day zero of the clipboard feature. thanks for your contribution!

@codspeed-hq
Copy link

codspeed-hq bot commented Dec 12, 2025

CodSpeed Performance Report

Merging #6037 will not alter performance

Comparing debangshu919:fix/rx-clipboard-paste-not-binding (d102bb2) with main (7826d0b)

Summary

✅ 8 untouched

debangshu919 and others added 2 commits December 13, 2025 15:20
Thanks for the suggestions!

Co-authored-by: Masen Furer <m_github@0x26.net>
@adhami3310 adhami3310 merged commit 224f920 into reflex-dev:main Dec 15, 2025
47 checks passed
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.

Bug Report: rx.clipboard paste event handler not binding in rx.dialog when rendered dynamically

3 participants