Merge pull request #10399 from 2betop/fix-store-sync

fix: 修复 store 数据上下层同步问题
This commit is contained in:
hsm-lv 2024-06-06 15:45:23 +08:00 committed by GitHub
commit 09e564e9e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 140 additions and 11 deletions

View File

@ -298,7 +298,7 @@ export function HocStoreFactory(renderer: {
props.store?.storeType === 'ComboStore'
? undefined
: syncDataFromSuper(
{...store.data, ...props.data},
{...store.pristineDiff, ...props.data},
(props.data as any).__super,
(prevProps.data as any).__super,
store,

View File

@ -25,7 +25,8 @@ export const iRendererStore = StoreNode.named('iRendererStore')
data: types.optional(types.frozen(), {}),
initedAt: 0, // 初始 init 的时刻
updatedAt: 0, // 从服务端更新时刻
pristine: types.optional(types.frozen(), {}),
pristine: types.optional(types.frozen(), {}), // pristine 的数据可能会被表单项的默认值form 的 initApi 等修改
upStreamData: types.optional(types.frozen(), {}), // 最原始的数据,只有由上游同步下来时才更新。用来判断是否变化过
action: types.optional(types.frozen(), undefined),
dialogOpen: false,
dialogData: types.optional(types.frozen(), undefined),
@ -39,6 +40,16 @@ export const iRendererStore = StoreNode.named('iRendererStore')
getPristineValueByName(name: string) {
return getVariable(self.pristine, name, false);
},
get pristineDiff() {
const data: any = {};
Object.keys(self.pristine).forEach(key => {
if (self.pristine[key] !== self.upStreamData[key]) {
data[key] = self.pristine[key];
}
});
return data;
}
}))
.actions(self => {
@ -63,6 +74,7 @@ export const iRendererStore = StoreNode.named('iRendererStore')
!skipSetPristine && (self.pristine = data);
self.data = data;
self.upStreamData = data;
},
reset() {

View File

@ -1784,3 +1784,115 @@ test('23. Nested CRUD change to normal CRUD', async () => {
const newSpace = container.querySelectorAll('.cxd-Table-expandSpace');
expect(newSpace.length).toEqual(0);
});
// CRUD 列中存在一列 List 组件List 是带 store 的crud 的行数据发生切换时,
// 如果 list 关联的数组,从有有成员变成 undefined 时,会出现 List 的数据不更新的问题。
// 原因是 withStore 里面同步逻辑有问题,保留了原来的 store.data
test('25. CRUD Table Cell sync data to store', async () => {
const {container} = render(
amisRender({
type: 'page',
id: 'page',
data: {
source: [
{
engine: 'Trident',
browser: 'Internet Explorer 5.0',
platform: 'Win 95+',
version: '5',
grade: 'C',
id: '1-1',
list: [{id: '1-1-1', name: '1-1-1'}]
},
{
engine: 'Trident',
browser: 'Internet Explorer 5.0',
platform: 'Win 95+',
version: '5',
grade: 'C',
id: '5'
}
]
},
body: [
{
type: 'button',
label: '切换数据源',
onEvent: {
click: {
actions: [
{
actionType: 'setValue',
componentId: 'page',
args: {
value: {
source: [
{
engine: 'Trident',
browser: 'Internet Explorer 4.0',
platform: 'Win 95+',
version: '4',
grade: 'X',
id: '3'
},
{
engine: 'Trident',
browser: 'Internet Explorer 4.0',
platform: 'Win 95+',
version: '4',
grade: 'X',
id: '4'
}
]
}
}
}
]
}
}
},
{
type: 'crud',
name: 'crud',
syncLocation: false,
source: '${source}',
draggable: true,
columns: [
{
name: 'id',
label: 'ID'
},
{
name: 'engine',
label: 'Rendering engine'
},
{
name: 'list',
label: 'List',
type: 'list',
source: '${list}',
listItem: {
title: '${id}-${name}'
}
}
]
}
]
})
);
await wait(300);
const button = container.querySelectorAll('.cxd-Button')[0];
// 刚开始存在 list 字段,所以是 1
const listDoms = container.querySelectorAll('.cxd-ListItem-title');
expect(listDoms.length).toEqual(1);
fireEvent.click(button);
await wait(300);
// 切换后 list 字段是 undefined 了,所以应该不显示了
const listDoms2 = container.querySelectorAll('.cxd-ListItem-title');
expect(listDoms2.length).toEqual(0);
});

View File

@ -26,6 +26,7 @@ exports[`store:ServiceStore 1`] = `
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"upStreamData": {},
"updatedAt": 0,
}
`;
@ -57,6 +58,7 @@ exports[`store:ServiceStore fetchInitData failed 1`] = `
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"upStreamData": {},
"updatedAt": 0,
},
{
@ -84,6 +86,7 @@ exports[`store:ServiceStore fetchInitData failed 1`] = `
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"upStreamData": {},
"updatedAt": 0,
},
]
@ -116,6 +119,7 @@ exports[`store:ServiceStore fetchInitData success 1`] = `
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"upStreamData": {},
},
{
"action": undefined,
@ -148,6 +152,7 @@ exports[`store:ServiceStore fetchInitData success 1`] = `
"schema": null,
"schemaKey": "",
"storeType": "ServiceStore",
"upStreamData": {},
},
]
`;

View File

@ -290,10 +290,10 @@ export default class Cards extends React.Component<GridProps, object> {
? resolveVariableAndFilter(source, prevProps.data, '| raw')
: null;
if (prev && prev === resolved) {
if (prev === resolved) {
updateItems = false;
} else if (Array.isArray(resolved)) {
items = resolved;
} else {
items = Array.isArray(resolved) ? resolved : [];
updateItems = true;
}
}

View File

@ -373,10 +373,10 @@ export default class List extends React.Component<ListProps, object> {
? resolveVariableAndFilter(source, prevProps.data, '| raw')
: null;
if (prev && prev === resolved) {
if (prev === resolved) {
updateItems = false;
} else if (Array.isArray(resolved)) {
items = resolved;
} else {
items = Array.isArray(resolved) ? resolved : [];
updateItems = true;
}
}

View File

@ -710,11 +710,11 @@ export default class Table extends React.Component<TableProps, object> {
? resolveVariableAndFilter(source, prevProps.data, '| raw')
: null;
if (prev && prev === resolved) {
if (prev === resolved) {
updateRows = false;
} else if (Array.isArray(resolved)) {
} else {
updateRows = true;
rows = resolved;
rows = Array.isArray(resolved) ? resolved : [];
}
}