fix(dataeditor): settle column defaults and the cell callback at create time
All Submissions:
- Have you followed the guidelines stated in CONTRIBUTING.md file?
- Have you checked to ensure there aren't any other open Pull Requests for the desired changed?
Type of change
- Bug fix (non-breaking change which fixes an issue)
Description
closes #7596
DataEditor.create only applied the documented default column type and id to literal column lists. State-backed columns reached Glide untouched, so a column without a type produced an invalid text cell and crashed at render.
This replaces add_hooks with hooks carried on the vars assigned to the props, all created in create:
- A state-backed column list is mapped through the defaults inside a
useMemokeyed on it, so the mapping runs when the columns change rather than for every cell Glide draws. It takes the same shapes as the literal path, a title string or an object, and the memoized var is typed as the column dicts the mapping yields, so alist[str]of titles passes the prop check. The columns prop names that memoized list and still tracks the state field it comes from. getCellContentis auseCallbackover the rendered columns and data. Both dependency arrays list the vars each prop reads, throughget_leaf_vars(#7606): the state fields, loop vars and memo props behind it, down to those inside a literal such as[[value]]or a predicate, and none of the constants. SoState.columns.filter(...)depends onState.columnsalone,State.data[1:]onState.data, and all-literal columns and data on nothing.formatDataEditorCellsis imported by the hook that uses it instead of byadd_imports.
Both hooks are created along with get_cell_content, so the component no longer mutates itself during rendering and nothing has to be stashed on the instance.
Column formatting moves into the data editor module as _format_column, which builds a new dict instead of modifying the caller's and fills in the default id only when none is given. reflex_base.utils.format.format_data_editor_column stays as a deprecated shim (0.10.1, removal 1.0) so an older data editor release keeps working against a newer base. The data editor is floored on reflex-base >= 0.10.1.dev0 for get_leaf_vars.
For rx.data_editor(columns=State.columns, data=State.data) the compiled component now holds:
const columns_x = useMemo(() => (state.columns).map((col) => typeof col === "string" ? { title: col, id: col.toLowerCase(), type: "str" } : { type: "str", id: (col.title ?? "").toLowerCase(), ...col }), [state.columns])
const getData_x = useCallback(([col, row]) => formatDataEditorCells(col, row, columns_x, state.data), [columns_x, state.data])
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?
tests/units/components/datadisplay/test_dataeditor.py covers the literal defaults (explicit values win, a column with an id needs no title, input not modified), the rejected inputs, the generated callback name, the callback with and without dependencies, a memo prop inside a literal, the state-backed hooks in order with preserved var data, a list[str] state var of titles, derived props (a filter and a slice) depending on the vars they read, a literal var memoized once, and a Node run of the generated hooks against the real formatDataEditorCells helper that asserts the normalized columns and the cells for dict and array rows. tests/units/reflex_base/utils/test_format.py covers the deprecation shim.
A real app with a state-backed grid whose column has no type was compiled and loaded in headless Chromium: the rows render, and adding a row and an untyped column re-renders with no page errors.
ruff, pyright on the touched files, make_pyi.py --check, and the unit tests pass. check_min_deps.py could not run in this environment (shallow clone without reachable release tags); the data editor uses only reflex-base APIs present in 0.10.0 plus get_leaf_vars, now on main.
🤖 Generated with Claude Code