feat(shiny): myIOProxy() partial-update API (B5)#82
Merged
Conversation
Adds an in-place data-update path for Shiny: myIOProxy(outputId, session) + updateMyIOData(proxy, label = df) send a 'myio:proxy-update' custom message that swaps a layer's data by label and re-renders through the existing data-join (Chart.updateData), instead of destroying+recreating the chart on every reactive invalidation. Preserves brush/zoom/toggle state and animates only changed marks. Wiring: an instance registry + idempotent handler in index.js, populated by the htmlwidget binding in shinyMode and torn down on re-render. Additive; non-Shiny and existing render paths are untouched. Tests: tests/js/myio-proxy.test.js (updateData swap + handler routing), tests/playwright/proxy.spec.ts (real re-render, same svg / no destroy), tests/testthat/test_myIOProxy.R (payload contract + validation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y preserve Addresses code + security review: - SECURITY (proto pollution): null-prototype maps + hasOwnProperty guard for Chart.updateData byLabel and window.myIO._instances, so a '__proto__' layer label or outputId cannot reach Object.prototype. - Registry leak/race: register the instance immediately after construction (before async coordinator work) so a re-render never leaves an empty window that drops an in-flight proxy message; unregister via the chart 'destroy' event; lazily reap destroyed charts (config nulled) in the message handler for the DOM-removed-without-rerender case. - Visibility: updateData no longer resets derived.currentLayers, so a legend-toggled-off subset stays hidden (mutating shared layer objects already updates the visible set) -- matches the 'preserves toggle state' claim. Tests: proto-pollution + visibility-preserve + dead-chart-reap units; remount+ proxy-update e2e; vi.resetModules() in the handler test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
B5 — Shiny in-place partial updates
myIOProxy(outputId, session)+updateMyIOData(proxy, label = df)update a rendered chart's layer data without re-runningrenderMyIO(). The previous reactive pattern destroyed and recreated the chart on every invalidation — flicker, lost brush/zoom/toggle state, full re-layout. The proxy swaps data by layer label and re-renders through the existing data-join path, so only changed marks transition and interaction state is preserved.Wiring
Chart.updateData([{label, data}])swaps layer data +renderCurrentLayers()(no destroy). An instance registry + a single idempotentmyio:proxy-updatehandler inindex.js, populated by the htmlwidget binding inshinyModeand unregistered on re-render.myIOProxy()(shiny-guarded, namespaces the id) +updateMyIOData()(row-rectangles eachdata.framevia the sameensure_source_key/as_layer_rowspath and sends the custom message).Additive — non-Shiny and existing render paths are untouched. Layers matched by label; unknown labels are ignored. Identity data path (transforms aren't re-applied; pass transformed data for transformed layers — documented).
Verification
npm test373 pass (incl.myio-proxy.test.js: data-swap contract + handler routing).npx playwright test proxy.spec.ts: real re-render adds marks, same<svg>node (no destroy), no errors.tests/testthat/test_myIOProxy.R: payload id/namespacing, row shape, multi-layer, validation.R CMD check --as-cran→ 0/0/0.🤖 Generated with Claude Code