Skip to content

Commit 0c81922

Browse files
committed
rx.upload must include _var_data from props (#4463)
* rx.upload must include _var_data from props str-casting the dropzone arguments removed any VarData they depended on, like the state context. update test_upload to include passing a prop from a state var * Handle large payload delta from upload event handler Fix update chunk chaining logic; try/catch wasn't catching errors from the async inner function.
1 parent 9c96023 commit 0c81922

File tree

3 files changed

+40
-22
lines changed

3 files changed

+40
-22
lines changed

reflex/.templates/web/utils/state.js

+23-16
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ export const connect = async (
457457
socket.current.on("reload", async (event) => {
458458
event_processing = false;
459459
queueEvents([...initialEvents(), JSON5.parse(event)], socket);
460-
})
460+
});
461461

462462
document.addEventListener("visibilitychange", checkVisibility);
463463
};
@@ -490,23 +490,30 @@ export const uploadFiles = async (
490490
return false;
491491
}
492492

493+
// Track how many partial updates have been processed for this upload.
493494
let resp_idx = 0;
494495
const eventHandler = (progressEvent) => {
495-
// handle any delta / event streamed from the upload event handler
496+
const event_callbacks = socket._callbacks.$event;
497+
// Whenever called, responseText will contain the entire response so far.
496498
const chunks = progressEvent.event.target.responseText.trim().split("\n");
499+
// So only process _new_ chunks beyond resp_idx.
497500
chunks.slice(resp_idx).map((chunk) => {
498-
try {
499-
socket._callbacks.$event.map((f) => {
500-
f(chunk);
501-
});
502-
resp_idx += 1;
503-
} catch (e) {
504-
if (progressEvent.progress === 1) {
505-
// Chunk may be incomplete, so only report errors when full response is available.
506-
console.log("Error parsing chunk", chunk, e);
507-
}
508-
return;
509-
}
501+
event_callbacks.map((f, ix) => {
502+
f(chunk)
503+
.then(() => {
504+
if (ix === event_callbacks.length - 1) {
505+
// Mark this chunk as processed.
506+
resp_idx += 1;
507+
}
508+
})
509+
.catch((e) => {
510+
if (progressEvent.progress === 1) {
511+
// Chunk may be incomplete, so only report errors when full response is available.
512+
console.log("Error parsing chunk", chunk, e);
513+
}
514+
return;
515+
});
516+
});
510517
});
511518
};
512519

@@ -711,7 +718,7 @@ export const useEventLoop = (
711718
const combined_name = events.map((e) => e.name).join("+++");
712719
if (event_actions?.temporal) {
713720
if (!socket.current || !socket.current.connected) {
714-
return; // don't queue when the backend is not connected
721+
return; // don't queue when the backend is not connected
715722
}
716723
}
717724
if (event_actions?.throttle) {
@@ -852,7 +859,7 @@ export const useEventLoop = (
852859
if (router.components[router.pathname].error) {
853860
delete router.components[router.pathname].error;
854861
}
855-
}
862+
};
856863
router.events.on("routeChangeStart", change_start);
857864
router.events.on("routeChangeComplete", change_complete);
858865
router.events.on("routeChangeError", change_error);

reflex/components/core/upload.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -293,20 +293,23 @@ def create(cls, *children, **props) -> Component:
293293
format.to_camel_case(key): value for key, value in upload_props.items()
294294
}
295295

296-
use_dropzone_arguments = {
297-
"onDrop": event_var,
298-
**upload_props,
299-
}
296+
use_dropzone_arguments = Var.create(
297+
{
298+
"onDrop": event_var,
299+
**upload_props,
300+
}
301+
)
300302

301303
left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} "
302-
right_side = f"useDropzone({str(Var.create(use_dropzone_arguments))})"
304+
right_side = f"useDropzone({str(use_dropzone_arguments)})"
303305

304306
var_data = VarData.merge(
305307
VarData(
306308
imports=Imports.EVENTS,
307309
hooks={Hooks.EVENTS: None},
308310
),
309311
event_var._get_all_var_data(),
312+
use_dropzone_arguments._get_all_var_data(),
310313
VarData(
311314
hooks={
312315
callback_str: None,

tests/integration/test_upload.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@ def UploadFile():
1919

2020
import reflex as rx
2121

22+
LARGE_DATA = "DUMMY" * 1024 * 512
23+
2224
class UploadState(rx.State):
2325
_file_data: Dict[str, str] = {}
2426
event_order: List[str] = []
2527
progress_dicts: List[dict] = []
28+
disabled: bool = False
29+
large_data: str = ""
2630

2731
async def handle_upload(self, files: List[rx.UploadFile]):
2832
for file in files:
@@ -33,6 +37,7 @@ async def handle_upload_secondary(self, files: List[rx.UploadFile]):
3337
for file in files:
3438
upload_data = await file.read()
3539
self._file_data[file.filename or ""] = upload_data.decode("utf-8")
40+
self.large_data = LARGE_DATA
3641
yield UploadState.chain_event
3742

3843
def upload_progress(self, progress):
@@ -41,13 +46,15 @@ def upload_progress(self, progress):
4146
self.progress_dicts.append(progress)
4247

4348
def chain_event(self):
49+
assert self.large_data == LARGE_DATA
50+
self.large_data = ""
4451
self.event_order.append("chain_event")
4552

4653
def index():
4754
return rx.vstack(
4855
rx.input(
4956
value=UploadState.router.session.client_token,
50-
is_read_only=True,
57+
read_only=True,
5158
id="token",
5259
),
5360
rx.heading("Default Upload"),
@@ -56,6 +63,7 @@ def index():
5663
rx.button("Select File"),
5764
rx.text("Drag and drop files here or click to select files"),
5865
),
66+
disabled=UploadState.disabled,
5967
),
6068
rx.button(
6169
"Upload",

0 commit comments

Comments
 (0)