fix(ui): reflect data-dependent field access changes without save/reload#17328
Open
Code-With-Farhan wants to merge 1 commit into
Open
fix(ui): reflect data-dependent field access changes without save/reload#17328Code-With-Farhan wants to merge 1 commit into
Code-With-Farhan wants to merge 1 commit into
Conversation
Code-With-Farhan
requested review from
AlessioGr,
JarrodMFlesch and
jacobsfletch
as code owners
July 15, 2026 03:57
Code-With-Farhan
force-pushed
the
fix/conditional-field-access-reactivity
branch
from
July 15, 2026 04:01
ba66db7 to
e70f502
Compare
Fixes payloadcms#12794 A field with a data-dependent `access.update` function (e.g. one that reads a sibling field's value) did not update its disabled/editable state as the user typed - it only reflected the correct state after saving and reloading the document. Three compounding bugs caused this: 1. `field.access.update` was never evaluated against the live, unsaved form data during the debounced onChange flow - only `field.access.read` was (in `addFieldStatePromise.ts`). Field-level update permissions came solely from `docPermissions`, which is only refetched after a save completes, so an in-progress edit's effect on a sibling field's access was invisible until save + reload. 2. Even once permissions.update correctly reflected the live data, `renderField` only re-renders a field when `renderAllFields` is true or its `lastRenderedPath` doesn't match the current path - it never accounted for a field's resolved `readOnly` state changing between requests, so an already-rendered field kept its stale disabled prop. 3. Even with a fresh render, Lexical's readOnly mode is only applied at initialization. `LexicalProvider` only remounts when its `key` changes, which previously only accounted for `initialValue` changes, not `readOnly`. Fixed by: 1. Evaluating `field.access.update` in `addFieldStatePromise.ts` against the live request data, mirroring the existing `access.read` check right above it. This runs inside the buildFormState request that already fires on every debounced onChange, so it adds no extra network request - unlike an earlier version of this fix that called the `/access/:id` endpoint from the client on every onChange, which broke an existing test asserting zero extra permission-check requests during typing (permissions should only be refetched after an explicit save, per `autosave - should not fetch new doc permissions on every autosave`). 2. Storing the last-rendered `readOnly` value on `FieldState` (new optional property) and forcing `renderField` to re-render when the newly computed `readOnly` differs from it. 3. Including `disabled` in `LexicalProvider`'s remount `key`, alongside the existing `path`/`rerenderProviderKey`. Added a regression test in test/form-state that reproduces the full onChange cycle (via `buildFormState` + the real `docAccessOperation`) and asserts both the resolved `readOnly` value and that the field's custom components are re-rendered under `renderAllFields: false`. Verified manually end-to-end: editing a sibling field now immediately unlocks/locks a richText field with a data-dependent `access.update`, with no save or reload required, in both directions.
Code-With-Farhan
force-pushed
the
fix/conditional-field-access-reactivity
branch
from
July 16, 2026 10:45
e70f502 to
2fdde95
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What?
A field with a data-dependent
access.updatefunction (e.g. one that reads a sibling field's value, like({ data }) => data?.title === 'test') didn't update its disabled/editable state as the user typed. It only reflected the correct state after saving and reloading the document.Why?
Fixes #12794
Three compounding bugs, all in the debounced onChange -> form-state round trip:
docPermissionswas only refetched after a save completed (Edit/index.tsx's save handler), never during the debounced onChange flow. Every field-change request kept reusing permissions computed from the last-saved data.renderFieldonly re-renders a field whenrenderAllFieldsis true or itslastRenderedPathdoesn't match the current path - it never accounted for a field's resolvedreadOnlystate changing between requests, so an already-rendered field kept its stale disabled prop.LexicalProvideronly remounts when itskeychanges, which previously only accounted forinitialValuechanges, notreadOnly.How?
useGetDocPermissionsnow returns its result instead of only setting state.Edit/index.tsx's onChange handler calls it with the live, unsaved form data on every change and feeds the fresh result into the samegetFormStatecall - mirroring the existinggetDocPreferencespattern that's already called fresh on every onChange.renderFieldnow stores the last-renderedreadOnlyvalue onFieldState(new optional property) and forces a re-render when the newly computedreadOnlydiffers from it, in addition to the existingrenderAllFields/lastRenderedPathconditions.richtext-lexical'sField.tsxnow includesdisabledin theLexicalProvider's remountkey, alongside the existingpath/rerenderProviderKey.Added a regression test in
test/form-statethat reproduces the full onChange cycle (viabuildFormState+ the realdocAccessOperation, not a hand-built permissions object) and asserts both the resolvedreadOnlyvalue and that the field's custom components are re-rendered underrenderAllFields: false.Verification
pnpm build:core): 46/46 packages passtsc --noEmiton every touched package: 0 errorspnpm test:int form-state field-access-context lexical: 230/230 passing (including the new regression test)access.update, with no save or reload required, in both directionsBefore: field stays locked after typing "test" into the sibling field, until save + reload.


After: typing directly into the field works immediately, with the Save button showing unsaved changes - proving no save/reload occurred.