Skip to content

Commit cd1f7f2

Browse files
committed
Address, document occasional renderUI()-related race condition
1 parent 342466c commit cd1f7f2

File tree

1 file changed

+23
-4
lines changed

1 file changed

+23
-4
lines changed

srcjs/input.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,28 @@ export function reactShinyInput(selector,
8787
return this.getInputValue(el);
8888
}
8989
setValue(el, value, rateLimited = false) {
90-
this.setInputValue(el, value);
91-
this.getCallback(el)(rateLimited);
92-
this.render(el);
90+
/*
91+
* We have to check whether $(el).data('callback') is undefined here
92+
* in case shiny::renderUI() is involved. If an input is contained in a
93+
* shiny::uiOutput(), the following strange thing happens occasionally:
94+
*
95+
* 1. setValue() is bound to an el in this.render(), below
96+
* 2. An event that will call setValue() is enqueued
97+
* 3. While the event is still enqueued, el is unbound and removed
98+
* from the DOM by the JS code associated with shiny::uiOutput()
99+
* - That code uses jQuery .html() in output_binding_html.js
100+
* - .html() removes el from the DOM and clears ist data and events
101+
* 4. By the time the setValue() bound to the original el is invoked,
102+
* el has been unbound and its data cleared.
103+
*
104+
* Since the original input is gone along with its callback, it
105+
* seems to make the most sense to do nothing.
106+
*/
107+
if ($(el).data('callback') !== undefined) {
108+
this.setInputValue(el, value);
109+
this.getCallback(el)(rateLimited);
110+
this.render(el);
111+
}
93112
}
94113
initialize(el) {
95114
$(el).data('value', JSON.parse($(el).next().text()));
@@ -99,7 +118,7 @@ export function reactShinyInput(selector,
99118
$(el).data('callback', callback);
100119
this.render(el);
101120
}
102-
unsubscribe(el, callback) {
121+
unsubscribe(el) {
103122
ReactDOM.render(null, el);
104123
}
105124
receiveMessage(el, data) {

0 commit comments

Comments
 (0)