From 0c02d492ab10a68f987a5c2127cda2f3197ec53a Mon Sep 17 00:00:00 2001 From: Alexey Morozov Date: Sat, 28 Mar 2026 16:49:58 +0300 Subject: [PATCH] Fix stale/non-saved data when calling `applyChanges()` immediately after `updateData()` in `EntityEditor` and `RelationEditor` --- CHANGELOG.md | 2 ++ src/editor/dataLocaleProvider.ts | 4 +++- src/widgets/editorForms/editEntityForm.tsx | 12 ++++++++++-- src/widgets/editorForms/editRelationForm.tsx | 14 +++++++++++--- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5270d7..189a3d11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to the Reactodia will be documented in this document. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +#### 🐛 Fixed +- Fix stale/non-saved data when calling `applyChanges()` immediately after `updateData()` in `EntityEditor` and `RelationEditor`. ## [0.34.0] - 2026-03-25 #### 🚀 New Features diff --git a/src/editor/dataLocaleProvider.ts b/src/editor/dataLocaleProvider.ts index 73558740..8b9109b5 100644 --- a/src/editor/dataLocaleProvider.ts +++ b/src/editor/dataLocaleProvider.ts @@ -195,7 +195,9 @@ export class DefaultDataLocaleProvider implements DataLocaleProvider { * } * ``` */ - prepareAnchor(targetIri: string): Pick, 'href' | 'target' | 'rel' | 'onClick'> { + prepareAnchor( + targetIri: string + ): Pick, 'draggable' | 'href' | 'target' | 'rel' | 'onClick'> { return { href: targetIri, target: '_blank', diff --git a/src/widgets/editorForms/editEntityForm.tsx b/src/widgets/editorForms/editEntityForm.tsx index 85096eba..4379593d 100644 --- a/src/widgets/editorForms/editEntityForm.tsx +++ b/src/widgets/editorForms/editEntityForm.tsx @@ -1,5 +1,6 @@ import cx from 'clsx'; import * as React from 'react'; +import { flushSync } from 'react-dom'; import { useTranslation } from '../../coreUtils/i18n'; import { useAsync } from '../../coreUtils/hooks'; @@ -60,7 +61,7 @@ export function EntityEditor(props: { /** * Render function to make an editor UI using provided state. */ - children: (props: EntityEditorProvidedProps) => React.ReactNode; + children: (props: EntityEditorProvidedProps) => React.ReactElement; }) { const {target, children} = props; const {editor} = useWorkspace(); @@ -74,7 +75,14 @@ export function EntityEditor(props: { data, updateData: setData, applyChanges: () => { - editor.changeEntity(target.data, data); + let finalData: ElementModel = data; + flushSync(() => { + setData(previous => { + finalData = previous; + return previous; + }); + }); + editor.changeEntity(target.data, finalData); }, }); } diff --git a/src/widgets/editorForms/editRelationForm.tsx b/src/widgets/editorForms/editRelationForm.tsx index e544b981..64f0fe8c 100644 --- a/src/widgets/editorForms/editRelationForm.tsx +++ b/src/widgets/editorForms/editRelationForm.tsx @@ -1,5 +1,6 @@ import cx from 'clsx'; import * as React from 'react'; +import { flushSync } from 'react-dom'; import { useTranslation } from '../../coreUtils/i18n'; import { useAsync } from '../../coreUtils/hooks'; @@ -96,7 +97,7 @@ export function RelationEditor(props: { /** * Render function to make an editor UI using provided state. */ - children: (props: RelationEditorProvidedProps) => React.ReactNode; + children: (props: RelationEditorProvidedProps) => React.ReactElement; }) { const {relation, onChangeTarget, children} = props; const {model} = useWorkspace(); @@ -223,11 +224,18 @@ function RelationEditorInner(props: { }); }, applyChanges: () => { - const toApply = dataFromExtendedLink(value.link); + let finalValue: ValidatedLink = value; + flushSync(() => { + setValue(previous => { + finalValue = previous; + return previous; + }); + }); + const toApply = dataFromExtendedLink(finalValue.link); if (editor.temporaryState.links.has(original.data)) { editor.removeTemporaryCells([original]); - const linkBase = relationFromExtendedLink(value.link, originalSource, originalTarget); + const linkBase = relationFromExtendedLink(finalValue.link, originalSource, originalTarget); editor.createRelation(linkBase); } else if (!( equalLinks(original.data, toApply) &&