Multi-value form data and TypedDict coercion; deprecate id-based form fields
Merged
MA
2d ago+2561+2561+0123456789012345678901234567890123456789-78-78-01234567890123456789
A form submitted every value of a repeated name (a checkbox group, hidden inputs that share a name, a two-thumb slider), but Object.fromEntries kept only the last one, so handlers never saw the rest. The form's ordered entries now reach the backend, and the handler's annotation decides what it receives:
rx.form.FormData[str, str]: a read-only mapping that keeps every value.form_data["x"]returns the last value;getlist("x"), or its aliasgetAll, returns all of them.- A
TypedDict:list[str]fields collect every value of their name. A list fieldrangealso collects therange[]values that a two-thumb slider submits.boolfields areFalsewhen nothing was submitted or the value is false-like ("false","off","no","n","0", empty).X | Nonefields areNonewhen nothing was submitted; unsubmittedNotRequiredfields are left out.- Required/NotRequired are now resolved correctly under postponed annotations, and generic TypedDicts are specialized.
dictor no annotation: unchanged, the last value of each name, as on main.
This PR is non-breaking. The removal of id-based form fields is split into the stacked reflex-dev/reflex#7540 for 0.11.0:
- A control with an
idstill adds a field read from its ref, and that value still replaces the value submitted under the samename. A name the form submits more than once keeps all its values. - Compiling a form whose handler takes the form data now emits a deprecation warning (deprecated in 0.10.1, removed in 0.11.0) for each value-bearing control with a static
idbut noname.
How it works:
getFormDatainstate.jscollects the FormData entries, applies the id-ref values, and returns a plain object for client-side use.- The entries are wrapped as
{"__reflex_form_data__": [...]}just before the event is emitted. reflex_base.utils.form.transform_form_databuilds the argument from its annotation. A FormData passed on to another handler is converted the same way.rx.formis now stubbed as a class, sorx.form.FormData[...]works in type annotations.
Fixes #7342
Type of change
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- This change requires a documentation update
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?
🤖 Generated with Claude Code