Repository: dotnet/aspnetcore
File: src/Components/Components/src/RenderTree/Renderer.cs (C#)

Code before:
            _eventBindings.Add(id, (renderedByComponentId, new EventCallback(@delegate.Target as IHandleEvent, @delegate)));
        }

        // NOTE: we do not to handle EventCallback<T> here. EventCallback<T> is only used when passing
        // a callback to a component, and never when used to attaching a DOM event handler.

        frame.AttributeEventHandlerIdField = id;
    }

    /// <summary>
    /// Schedules a render for the specified <paramref name="componentId"/>. Its display
    /// will be populated using the specified <paramref name="renderFragment"/>.
    /// </summary>
    /// <param name="componentId">The ID of the component to render.</param>
    /// <param name="renderFragment">A <see cref="RenderFragment"/> that will supply the updated UI contents.</param>
    internal void AddToRenderQueue(int componentId, RenderFragment renderFragment)
    {
        Dispatcher.AssertAccess();

        var componentState = GetOptionalComponentState(componentId);
        if (componentState == null)
        {
            // If the component was already disposed, then its render handle trying to
            // queue a render is a no-op.
            return;
        }

        _batchBuilder.ComponentRenderQueue.Enqueue(
            new RenderQueueEntry(componentState, renderFragment));

        if (!_isBatchInProgress)
        {
            ProcessPendingRender();
        }
    }

    internal void TrackReplacedEventHandlerId(ulong oldEventHandlerId, ulong newEventHandlerId)
    {
        // Tracking the chain of old->new replacements allows us to interpret incoming EventFieldInfo
        // values even if they refer to an event handler ID that's since been superseded. This is essential
        // for tree patching to work in an async environment.
        _eventHandlerIdReplacements.Add(oldEventHandlerId, newEventHandlerId);
    }

    private (int RenderedByComponentId, EventCallback Callback) GetRequiredEventBindingEntry(ulong eventHandlerId)
    {
        if (!_eventBindings.TryGetValue(eventHandlerId, out var entry))
        {
            throw new ArgumentException($"There is no event handler associated with this event. EventId: '{eventHandlerId}'.", nameof(eventHandlerId));
        }


Review comment (suggestion):
I think we should do this inside `https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/RenderTree/Renderer.cs#L736` which can be overriden by `EndpointHtmlRenderer` and save ourselves having the public API `SignalRendererToFinishRendering`

Code after:
            // render tree.
            _eventBindings.Add(id, (renderedByComponentId, new EventCallback(@delegate.Target as IHandleEvent, @delegate)));
        }

        // NOTE: we do not to handle EventCallback<T> here. EventCallback<T> is only used when passing
        // a callback to a component, and never when used to attaching a DOM event handler.

        frame.AttributeEventHandlerIdField = id;
    }

    /// <summary>
    /// Schedules a render for the specified <paramref name="componentId"/>. Its display
    /// will be populated using the specified <paramref name="renderFragment"/>.
    /// </summary>
    /// <param name="componentId">The ID of the component to render.</param>
    /// <param name="renderFragment">A <see cref="RenderFragment"/> that will supply the updated UI contents.</param>
    internal void AddToRenderQueue(int componentId, RenderFragment renderFragment)
    {
        Dispatcher.AssertAccess();

        if (_rendererIsStopped)
        {
            // Once we're stopped, we'll disregard further attempts to queue anything
            return;
        }

        var componentState = GetOptionalComponentState(componentId);
        if (componentState == null)
        {
            // If the component was already disposed, then its render handle trying to
            // queue a render is a no-op.
            return;
        }

        _batchBuilder.ComponentRenderQueue.Enqueue(
            new RenderQueueEntry(componentState, renderFragment));

        if (!_isBatchInProgress)
        {
            ProcessPendingRender();
        }
    }

    internal void TrackReplacedEventHandlerId(ulong oldEventHandlerId, ulong newEventHandlerId)
    {
        // Tracking the chain of old->new replacements allows us to interpret incoming EventFieldInfo
        // values even if they refer to an event handler ID that's since been superseded. This is essential
        // for tree patching to work in an async environment.
        _eventHandlerIdReplacements.Add(oldEventHandlerId, newEventHandlerId);
    }