fix(dataeditor): settle column defaults and the cell callback at create time

MA
6h ago
+591-94

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 useMemo keyed 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 a list[str] of titles passes the prop check. The columns prop names that memoized list and still tracks the state field it comes from.
  • getCellContent is a useCallback over the rendered columns and data. Both dependency arrays list the vars each prop reads, through get_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. So State.columns.filter(...) depends on State.columns alone, State.data[1:] on State.data, and all-literal columns and data on nothing.
  • formatDataEditorCells is imported by the hook that uses it instead of by add_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

https://claude.ai/code/session_01RqJ6TNt8AXzbDVm5kLb2nD