Skip to content

Commit 973a23c

Browse files
committed
brings patch to side-updates and allows for use in clientside
(cherry picked from commit 20d930f)
1 parent 89a08e7 commit 973a23c

File tree

3 files changed

+181
-4
lines changed

3 files changed

+181
-4
lines changed

dash/dash-renderer/src/actions/callbacks.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {createAction, Action} from 'redux-actions';
4141
import {addHttpHeaders} from '../actions';
4242
import {notifyObservers, updateProps} from './index';
4343
import {CallbackJobPayload} from '../reducers/callbackJobs';
44-
import {handlePatch, isPatch} from './patch';
44+
import {handlePatch, isPatch, parsePatchProps} from './patch';
4545
import {computePaths, getPath} from './paths';
4646

4747
import {requestDependencies} from './requestDependencies';
@@ -419,7 +419,6 @@ function sideUpdate(outputs: SideUpdateOutput, cb: ICallbackPayload) {
419419
}, [] as any[])
420420
.forEach(([id, idProps]) => {
421421
const state = getState();
422-
dispatch(updateComponent(id, idProps, cb));
423422

424423
const componentPath = getPath(state.paths, id);
425424
if (!componentPath) {
@@ -429,12 +428,18 @@ function sideUpdate(outputs: SideUpdateOutput, cb: ICallbackPayload) {
429428
}
430429
const oldComponent = getComponentLayout(componentPath, state);
431430

431+
const oldProps = oldComponent?.props || {};
432+
433+
const patchedProps = parsePatchProps(idProps, oldProps)
434+
435+
dispatch(updateComponent(id, patchedProps, cb));
436+
432437
dispatch(
433438
setPaths(
434439
computePaths(
435440
{
436441
...oldComponent,
437-
props: {...oldComponent.props, ...idProps}
442+
props: {...oldComponent.props, ...patchedProps}
438443
},
439444
[...componentPath],
440445
state.paths,

dash/dash-renderer/src/actions/patch.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,143 @@ function getLocationPath(location: LocationIndex[], obj: any) {
4444
return current;
4545
}
4646

47+
export class PatchBuilder {
48+
private operations: PatchOperation[] = [];
49+
50+
assign(location: LocationIndex[], value: any) {
51+
this.operations.push({
52+
operation: 'Assign',
53+
location,
54+
params: { value }
55+
});
56+
return this;
57+
}
58+
59+
merge(location: LocationIndex[], value: any) {
60+
this.operations.push({
61+
operation: 'Merge',
62+
location,
63+
params: { value }
64+
});
65+
return this;
66+
}
67+
68+
extend(location: LocationIndex[], value: any) {
69+
this.operations.push({
70+
operation: 'Extend',
71+
location,
72+
params: { value }
73+
});
74+
return this;
75+
}
76+
77+
delete(location: LocationIndex[]) {
78+
this.operations.push({
79+
operation: 'Delete',
80+
location,
81+
params: {}
82+
});
83+
return this;
84+
}
85+
86+
insert(location: LocationIndex[], index: number, value: any) {
87+
this.operations.push({
88+
operation: 'Insert',
89+
location,
90+
params: { index, value }
91+
});
92+
return this;
93+
}
94+
95+
append(location: LocationIndex[], value: any) {
96+
this.operations.push({
97+
operation: 'Append',
98+
location,
99+
params: { value }
100+
});
101+
return this;
102+
}
103+
104+
prepend(location: LocationIndex[], value: any) {
105+
this.operations.push({
106+
operation: 'Prepend',
107+
location,
108+
params: { value }
109+
});
110+
return this;
111+
}
112+
113+
add(location: LocationIndex[], value: any) {
114+
this.operations.push({
115+
operation: 'Add',
116+
location,
117+
params: { value }
118+
});
119+
return this;
120+
}
121+
122+
sub(location: LocationIndex[], value: any) {
123+
this.operations.push({
124+
operation: 'Sub',
125+
location,
126+
params: { value }
127+
});
128+
return this;
129+
}
130+
131+
mul(location: LocationIndex[], value: any) {
132+
this.operations.push({
133+
operation: 'Mul',
134+
location,
135+
params: { value }
136+
});
137+
return this;
138+
}
139+
140+
div(location: LocationIndex[], value: any) {
141+
this.operations.push({
142+
operation: 'Div',
143+
location,
144+
params: { value }
145+
});
146+
return this;
147+
}
148+
149+
clear(location: LocationIndex[]) {
150+
this.operations.push({
151+
operation: 'Clear',
152+
location,
153+
params: {}
154+
});
155+
return this;
156+
}
157+
158+
reverse(location: LocationIndex[]) {
159+
this.operations.push({
160+
operation: 'Reverse',
161+
location,
162+
params: {}
163+
});
164+
return this;
165+
}
166+
167+
remove(location: LocationIndex[], value: any) {
168+
this.operations.push({
169+
operation: 'Remove',
170+
location,
171+
params: { value }
172+
});
173+
return this;
174+
}
175+
176+
build() {
177+
return {
178+
__dash_patch_update: true,
179+
operations: this.operations
180+
};
181+
}
182+
}
183+
47184
const patchHandlers: {[k: string]: PatchHandler} = {
48185
Assign: (previous, patchOperation) => {
49186
const {params, location} = patchOperation;
@@ -166,3 +303,26 @@ export function handlePatch<T>(previousValue: T, patchValue: any): T {
166303

167304
return reducedValue;
168305
}
306+
307+
export function parsePatchProps(props: any, previousProps: any): {} {
308+
if (!is(Object, props)) {
309+
return props;
310+
}
311+
312+
let patchedProps: any = {};
313+
314+
for (const key of Object.keys(props)) {
315+
const val = props[key];
316+
if (isPatch(val)) {
317+
const previousValue = previousProps[key];
318+
if (previousValue === undefined) {
319+
throw new Error('Cannot patch undefined');
320+
}
321+
patchedProps[key] = handlePatch(previousValue, val);
322+
} else {
323+
patchedProps[key] = val;
324+
}
325+
}
326+
327+
return patchedProps;
328+
}

dash/dash-renderer/src/utils/clientsideFunctions.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {updateProps, notifyObservers, setPaths} from '../actions/index';
2+
import {parsePatchProps, PatchBuilder} from '../actions/patch';
23
import {computePaths, getPath} from '../actions/paths';
34
import {getComponentLayout} from '../wrapper/wrapping';
45
import {getStores} from './stores';
@@ -23,6 +24,16 @@ function set_props(
2324
} else {
2425
componentPath = idOrPath;
2526
}
27+
const oldComponent = getComponentLayout(componentPath, state);
28+
if (!oldComponent) {
29+
console.error(
30+
`Could not find component with id or path: ${idOrPath}`
31+
);
32+
return;
33+
}
34+
// Handle any patch props
35+
props = parsePatchProps(props, oldComponent.props);
36+
2637
dispatch(
2738
updateProps({
2839
props,
@@ -31,7 +42,7 @@ function set_props(
3142
})
3243
);
3344
dispatch(notifyObservers({id: idOrPath, props}));
34-
const oldComponent = getComponentLayout(componentPath, state);
45+
3546

3647
dispatch(
3748
setPaths(
@@ -77,3 +88,4 @@ const dc = ((window as any).dash_clientside =
7788
(window as any).dash_clientside || {});
7889
dc['set_props'] = set_props;
7990
dc['clean_url'] = dc['clean_url'] === undefined ? clean_url : dc['clean_url'];
91+
dc['Patch'] = PatchBuilder;

0 commit comments

Comments
 (0)