@etsoo/react 1.1.85 → 1.1.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/mu/NotifierMUTests.tsx +20 -6
- package/lib/app/ReactApp.d.ts +3 -0
- package/lib/app/ReactApp.js +3 -0
- package/lib/components/GridLoader.d.ts +0 -4
- package/lib/components/ScrollerGrid.js +3 -5
- package/lib/components/ScrollerList.js +3 -5
- package/lib/mu/NotifierMU.js +1 -1
- package/lib/mu/TableEx.js +7 -1
- package/lib/mu/Tiplist.js +12 -0
- package/lib/mu/pages/CommonPage.js +12 -7
- package/lib/mu/pages/CommonPageProps.d.ts +7 -3
- package/package.json +4 -4
- package/src/app/ReactApp.ts +3 -0
- package/src/components/GridLoader.ts +0 -5
- package/src/components/ScrollerGrid.tsx +3 -4
- package/src/components/ScrollerList.tsx +3 -4
- package/src/mu/NotifierMU.tsx +1 -1
- package/src/mu/TableEx.tsx +8 -1
- package/src/mu/Tiplist.tsx +14 -0
- package/src/mu/pages/CommonPage.tsx +7 -2
- package/src/mu/pages/CommonPageProps.ts +8 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Done from '@mui/icons-material/Done';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import ReactDOM from 'react-dom';
|
|
3
4
|
import { act } from 'react-dom/test-utils';
|
|
@@ -33,7 +34,11 @@ test('Alert tests', () => {
|
|
|
33
34
|
|
|
34
35
|
expect(button.length).toBe(1);
|
|
35
36
|
expect(button[0].innerHTML).toContain('OK');
|
|
36
|
-
|
|
37
|
+
|
|
38
|
+
act(() => {
|
|
39
|
+
button[0].click();
|
|
40
|
+
});
|
|
41
|
+
|
|
37
42
|
expect(handleClick).toBeCalled();
|
|
38
43
|
|
|
39
44
|
// Fast forward
|
|
@@ -54,7 +59,11 @@ test('Confirm tests', () => {
|
|
|
54
59
|
|
|
55
60
|
expect(button.length).toBe(2);
|
|
56
61
|
expect(button[0].innerHTML).toContain('Cancel');
|
|
57
|
-
|
|
62
|
+
|
|
63
|
+
act(() => {
|
|
64
|
+
button[0].click();
|
|
65
|
+
});
|
|
66
|
+
|
|
58
67
|
expect(handleClick).toBeCalled();
|
|
59
68
|
|
|
60
69
|
// Fast forward
|
|
@@ -63,11 +72,13 @@ test('Confirm tests', () => {
|
|
|
63
72
|
|
|
64
73
|
test('Prompt tests', () => {
|
|
65
74
|
// Click
|
|
66
|
-
const handleClick = jest.fn()
|
|
75
|
+
const handleClick = jest.fn((result: boolean) => {
|
|
76
|
+
expect(result).toBeTruthy();
|
|
77
|
+
});
|
|
67
78
|
|
|
68
79
|
act(() => {
|
|
69
80
|
// Add the notification
|
|
70
|
-
notifier.prompt('Prompt message', handleClick, undefined, {
|
|
81
|
+
notifier.prompt<boolean>('Prompt message', handleClick, undefined, {
|
|
71
82
|
type: 'switch'
|
|
72
83
|
});
|
|
73
84
|
});
|
|
@@ -77,10 +88,13 @@ test('Prompt tests', () => {
|
|
|
77
88
|
|
|
78
89
|
expect(button.length).toBe(2); // Switch will generate a button
|
|
79
90
|
expect(button[1].innerHTML).toContain('OK');
|
|
80
|
-
|
|
91
|
+
|
|
92
|
+
act(() => {
|
|
93
|
+
button[1].click();
|
|
94
|
+
});
|
|
95
|
+
|
|
81
96
|
expect(handleClick).toBeCalled();
|
|
82
97
|
|
|
83
|
-
// Fast forward
|
|
84
98
|
jest.runOnlyPendingTimers();
|
|
85
99
|
});
|
|
86
100
|
|
package/lib/app/ReactApp.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ import { UserAction, UserState } from '../states/UserState';
|
|
|
8
8
|
import { InputDialogProps } from './InputDialogProps';
|
|
9
9
|
/**
|
|
10
10
|
* React app state detector
|
|
11
|
+
* Case 1: undefined, when refresh the whole page
|
|
12
|
+
* Case 2: false, unauthorized
|
|
13
|
+
* Case 3: true, authorized or considered as authorized (maynot, like token expiry)
|
|
11
14
|
* @param props Props
|
|
12
15
|
* @returns Component
|
|
13
16
|
*/
|
package/lib/app/ReactApp.js
CHANGED
|
@@ -5,6 +5,9 @@ import { UserActionType, UserState } from '../states/UserState';
|
|
|
5
5
|
let app;
|
|
6
6
|
/**
|
|
7
7
|
* React app state detector
|
|
8
|
+
* Case 1: undefined, when refresh the whole page
|
|
9
|
+
* Case 2: false, unauthorized
|
|
10
|
+
* Case 3: true, authorized or considered as authorized (maynot, like token expiry)
|
|
8
11
|
* @param props Props
|
|
9
12
|
* @returns Component
|
|
10
13
|
*/
|
|
@@ -10,14 +10,11 @@ export const ScrollerGrid = (props) => {
|
|
|
10
10
|
const { autoLoad = true, defaultOrderBy, defaultOrderByAsc, footerRenderer, headerRenderer, itemRenderer, idField = 'id', loadBatchSize, loadData, mRef, onItemsRendered, onSelectChange, rowHeight = 53, threshold = 6, width, ...rest } = props;
|
|
11
11
|
// States
|
|
12
12
|
const [state, stateUpdate] = React.useReducer((currentState, newState) => {
|
|
13
|
-
if (!currentState.mounted)
|
|
14
|
-
return currentState;
|
|
15
13
|
return { ...currentState, ...newState };
|
|
16
14
|
}, {
|
|
17
15
|
autoLoad,
|
|
18
16
|
currentPage: 0,
|
|
19
17
|
hasNextPage: true,
|
|
20
|
-
mounted: true,
|
|
21
18
|
isNextPageLoading: false,
|
|
22
19
|
orderBy: defaultOrderBy,
|
|
23
20
|
orderByAsc: defaultOrderByAsc,
|
|
@@ -25,6 +22,7 @@ export const ScrollerGrid = (props) => {
|
|
|
25
22
|
batchSize: 10,
|
|
26
23
|
selectedItems: []
|
|
27
24
|
});
|
|
25
|
+
const isMounted = React.useRef(true);
|
|
28
26
|
const ref = React.createRef();
|
|
29
27
|
// Load data
|
|
30
28
|
const loadDataLocal = (pageAdd = 1) => {
|
|
@@ -44,7 +42,7 @@ export const ScrollerGrid = (props) => {
|
|
|
44
42
|
data
|
|
45
43
|
};
|
|
46
44
|
loadData(loadProps).then((result) => {
|
|
47
|
-
if (result == null || !
|
|
45
|
+
if (result == null || !isMounted.current) {
|
|
48
46
|
return;
|
|
49
47
|
}
|
|
50
48
|
const newItems = result.length;
|
|
@@ -173,7 +171,7 @@ export const ScrollerGrid = (props) => {
|
|
|
173
171
|
}), [state.rows, state.selectedItems]);
|
|
174
172
|
React.useEffect(() => {
|
|
175
173
|
return () => {
|
|
176
|
-
|
|
174
|
+
isMounted.current = false;
|
|
177
175
|
};
|
|
178
176
|
}, []);
|
|
179
177
|
// Destruct state
|
|
@@ -27,14 +27,11 @@ export const ScrollerList = (props) => {
|
|
|
27
27
|
const refs = useCombinedRefs(oRef, outerRef);
|
|
28
28
|
// States
|
|
29
29
|
const [state, stateUpdate] = React.useReducer((currentState, newState) => {
|
|
30
|
-
if (!currentState.mounted)
|
|
31
|
-
return currentState;
|
|
32
30
|
return { ...currentState, ...newState };
|
|
33
31
|
}, {
|
|
34
32
|
autoLoad,
|
|
35
33
|
currentPage: 0,
|
|
36
34
|
hasNextPage: true,
|
|
37
|
-
mounted: true,
|
|
38
35
|
isNextPageLoading: false,
|
|
39
36
|
orderBy: defaultOrderBy,
|
|
40
37
|
orderByAsc: defaultOrderByAsc,
|
|
@@ -42,6 +39,7 @@ export const ScrollerList = (props) => {
|
|
|
42
39
|
batchSize: GridSizeGet(loadBatchSize, height),
|
|
43
40
|
selectedItems: []
|
|
44
41
|
});
|
|
42
|
+
const isMounted = React.useRef(true);
|
|
45
43
|
// Load data
|
|
46
44
|
const loadDataLocal = (pageAdd = 1) => {
|
|
47
45
|
// Prevent multiple loadings
|
|
@@ -60,7 +58,7 @@ export const ScrollerList = (props) => {
|
|
|
60
58
|
data
|
|
61
59
|
};
|
|
62
60
|
loadData(loadProps).then((result) => {
|
|
63
|
-
if (result == null || !
|
|
61
|
+
if (result == null || !isMounted.current) {
|
|
64
62
|
return;
|
|
65
63
|
}
|
|
66
64
|
const newItems = result.length;
|
|
@@ -158,7 +156,7 @@ export const ScrollerList = (props) => {
|
|
|
158
156
|
window.cancelAnimationFrame(requestAnimationFrameSeed);
|
|
159
157
|
// Remove scroll event
|
|
160
158
|
window.removeEventListener('scroll', handleWindowScroll);
|
|
161
|
-
|
|
159
|
+
isMounted.current = false;
|
|
162
160
|
};
|
|
163
161
|
}, []);
|
|
164
162
|
// Destruct state
|
package/lib/mu/NotifierMU.js
CHANGED
|
@@ -132,7 +132,7 @@ export class NotificationMU extends NotificationReact {
|
|
|
132
132
|
result = this.onReturn(numberValue);
|
|
133
133
|
}
|
|
134
134
|
else if (type === 'switch') {
|
|
135
|
-
const boolValue = inputRef.current.value
|
|
135
|
+
const boolValue = inputRef.current.value === 'true';
|
|
136
136
|
result = this.onReturn(boolValue);
|
|
137
137
|
}
|
|
138
138
|
else {
|
package/lib/mu/TableEx.js
CHANGED
|
@@ -49,6 +49,7 @@ export function TableEx(props) {
|
|
|
49
49
|
batchSize: rowsPerPageLocal,
|
|
50
50
|
selectedItems: []
|
|
51
51
|
});
|
|
52
|
+
const isMounted = React.useRef(true);
|
|
52
53
|
// Reset the state and load again
|
|
53
54
|
const reset = (add) => {
|
|
54
55
|
const state = {
|
|
@@ -91,7 +92,7 @@ export function TableEx(props) {
|
|
|
91
92
|
data
|
|
92
93
|
};
|
|
93
94
|
loadData(loadProps).then((result) => {
|
|
94
|
-
if (result == null) {
|
|
95
|
+
if (!isMounted.current || result == null) {
|
|
95
96
|
return;
|
|
96
97
|
}
|
|
97
98
|
const newItems = result.length;
|
|
@@ -166,6 +167,11 @@ export function TableEx(props) {
|
|
|
166
167
|
// Auto load data when current page is 0
|
|
167
168
|
if (currentPage === 0 && stateAutoLoad && lastLoadedItems == null)
|
|
168
169
|
loadDataLocal();
|
|
170
|
+
React.useEffect(() => {
|
|
171
|
+
return () => {
|
|
172
|
+
isMounted.current = false;
|
|
173
|
+
};
|
|
174
|
+
}, []);
|
|
169
175
|
// Layout
|
|
170
176
|
return (React.createElement(Paper, null,
|
|
171
177
|
React.createElement(TableContainer, { sx: { maxHeight } },
|
package/lib/mu/Tiplist.js
CHANGED
|
@@ -27,6 +27,7 @@ export function Tiplist(props) {
|
|
|
27
27
|
});
|
|
28
28
|
// State
|
|
29
29
|
const [state] = React.useState({});
|
|
30
|
+
const isMounted = React.useRef(true);
|
|
30
31
|
// Add readOnly
|
|
31
32
|
const addReadOnly = (params) => {
|
|
32
33
|
if (readOnly != null) {
|
|
@@ -73,6 +74,8 @@ export function Tiplist(props) {
|
|
|
73
74
|
stateUpdate({ loading: true });
|
|
74
75
|
// Load list
|
|
75
76
|
loadData(keyword, id).then((options) => {
|
|
77
|
+
if (!isMounted.current)
|
|
78
|
+
return;
|
|
76
79
|
// Indicates loading completed
|
|
77
80
|
stateUpdate({
|
|
78
81
|
loading: false,
|
|
@@ -107,6 +110,15 @@ export function Tiplist(props) {
|
|
|
107
110
|
state.idLoaded = true;
|
|
108
111
|
}
|
|
109
112
|
}
|
|
113
|
+
React.useEffect(() => {
|
|
114
|
+
return () => {
|
|
115
|
+
// Clear any seed
|
|
116
|
+
if (state.loadDataSeed != null) {
|
|
117
|
+
clearTimeout(state.loadDataSeed);
|
|
118
|
+
}
|
|
119
|
+
isMounted.current = false;
|
|
120
|
+
};
|
|
121
|
+
}, []);
|
|
110
122
|
// Layout
|
|
111
123
|
return (React.createElement("div", null,
|
|
112
124
|
React.createElement("input", { ref: inputRef, "data-reset": "true", type: "text", style: { display: 'none' }, name: name, defaultValue: localIdValue }),
|
|
@@ -22,7 +22,7 @@ export const CommonPageScrollContainer = global;
|
|
|
22
22
|
*/
|
|
23
23
|
export function CommonPage(props) {
|
|
24
24
|
// Destruct
|
|
25
|
-
const { children, disableGutters = true, fabButtons, fabSize = 'medium', maxWidth = false, moreActions, onRefresh, onUpdate, paddings = MUGlobal.pagePaddings, scrollContainer, pullContainer, sx = {}, ...rest } = props;
|
|
25
|
+
const { children, disableGutters = true, fabButtons, fabSize = 'medium', maxWidth = false, moreActions, onRefresh, onUpdate, onUpdateAll, paddings = MUGlobal.pagePaddings, scrollContainer, pullContainer, sx = {}, ...rest } = props;
|
|
26
26
|
// Merge style
|
|
27
27
|
Object.assign(sx, {
|
|
28
28
|
padding: paddings
|
|
@@ -30,14 +30,19 @@ export function CommonPage(props) {
|
|
|
30
30
|
// Labels
|
|
31
31
|
const labels = Labels.CommonPage;
|
|
32
32
|
// Update
|
|
33
|
-
const update =
|
|
34
|
-
?
|
|
35
|
-
:
|
|
33
|
+
const update = onUpdateAll
|
|
34
|
+
? onUpdateAll
|
|
35
|
+
: onUpdate
|
|
36
36
|
? (authorized) => {
|
|
37
|
-
if (authorized)
|
|
38
|
-
|
|
37
|
+
if (authorized == null || authorized)
|
|
38
|
+
onUpdate();
|
|
39
39
|
}
|
|
40
|
-
:
|
|
40
|
+
: onRefresh
|
|
41
|
+
? (authorized) => {
|
|
42
|
+
if (authorized)
|
|
43
|
+
onRefresh();
|
|
44
|
+
}
|
|
45
|
+
: undefined;
|
|
41
46
|
// Return the UI
|
|
42
47
|
return (React.createElement(React.Fragment, null,
|
|
43
48
|
update && React.createElement(ReactAppStateDetector, { update: update }),
|
|
@@ -21,13 +21,17 @@ export interface CommonPageProps extends Omit<ContainerProps, 'id'> {
|
|
|
21
21
|
*/
|
|
22
22
|
moreActions?: MoreAction[];
|
|
23
23
|
/**
|
|
24
|
-
* On refresh callback
|
|
24
|
+
* On refresh callback, only when authorized = true
|
|
25
25
|
*/
|
|
26
26
|
onRefresh?: () => void | PromiseLike<void>;
|
|
27
27
|
/**
|
|
28
|
-
* On page update, may uses onRefresh
|
|
28
|
+
* On page update, when authorized = null or true case, may uses onRefresh
|
|
29
29
|
*/
|
|
30
|
-
onUpdate?:
|
|
30
|
+
onUpdate?: () => void | PromiseLike<void>;
|
|
31
|
+
/**
|
|
32
|
+
* On page update, all cases with authorized
|
|
33
|
+
*/
|
|
34
|
+
onUpdateAll?: IStateUpdate;
|
|
31
35
|
/**
|
|
32
36
|
* Paddings
|
|
33
37
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/react",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.89",
|
|
4
4
|
"description": "TypeScript ReactJs framework",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"@emotion/react": "^11.4.1",
|
|
45
45
|
"@emotion/style": "^0.8.0",
|
|
46
46
|
"@emotion/styled": "^11.3.0",
|
|
47
|
-
"@etsoo/appscript": "^1.0.
|
|
47
|
+
"@etsoo/appscript": "^1.0.88",
|
|
48
48
|
"@etsoo/notificationbase": "^1.0.78",
|
|
49
49
|
"@etsoo/shared": "^1.0.41",
|
|
50
|
-
"@mui/icons-material": "^5.0.0
|
|
51
|
-
"@mui/material": "^5.0.0
|
|
50
|
+
"@mui/icons-material": "^5.0.0",
|
|
51
|
+
"@mui/material": "^5.0.0",
|
|
52
52
|
"@reach/router": "^1.3.4",
|
|
53
53
|
"@types/pica": "^5.1.3",
|
|
54
54
|
"@types/pulltorefreshjs": "^0.1.5",
|
package/src/app/ReactApp.ts
CHANGED
|
@@ -23,6 +23,9 @@ let app: IReactApp<IAppSettings, any>;
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* React app state detector
|
|
26
|
+
* Case 1: undefined, when refresh the whole page
|
|
27
|
+
* Case 2: false, unauthorized
|
|
28
|
+
* Case 3: true, authorized or considered as authorized (maynot, like token expiry)
|
|
26
29
|
* @param props Props
|
|
27
30
|
* @returns Component
|
|
28
31
|
*/
|
|
@@ -164,14 +164,12 @@ export const ScrollerGrid = <T extends Record<string, any>>(
|
|
|
164
164
|
currentState: GridLoaderStates<T>,
|
|
165
165
|
newState: Partial<GridLoaderStates<T>>
|
|
166
166
|
) => {
|
|
167
|
-
if (!currentState.mounted) return currentState;
|
|
168
167
|
return { ...currentState, ...newState };
|
|
169
168
|
},
|
|
170
169
|
{
|
|
171
170
|
autoLoad,
|
|
172
171
|
currentPage: 0,
|
|
173
172
|
hasNextPage: true,
|
|
174
|
-
mounted: true,
|
|
175
173
|
isNextPageLoading: false,
|
|
176
174
|
orderBy: defaultOrderBy,
|
|
177
175
|
orderByAsc: defaultOrderByAsc,
|
|
@@ -180,6 +178,7 @@ export const ScrollerGrid = <T extends Record<string, any>>(
|
|
|
180
178
|
selectedItems: []
|
|
181
179
|
}
|
|
182
180
|
);
|
|
181
|
+
const isMounted = React.useRef(true);
|
|
183
182
|
|
|
184
183
|
const ref = React.createRef<VariableSizeGrid<T>>();
|
|
185
184
|
|
|
@@ -204,7 +203,7 @@ export const ScrollerGrid = <T extends Record<string, any>>(
|
|
|
204
203
|
};
|
|
205
204
|
|
|
206
205
|
loadData(loadProps).then((result) => {
|
|
207
|
-
if (result == null || !
|
|
206
|
+
if (result == null || !isMounted.current) {
|
|
208
207
|
return;
|
|
209
208
|
}
|
|
210
209
|
|
|
@@ -360,7 +359,7 @@ export const ScrollerGrid = <T extends Record<string, any>>(
|
|
|
360
359
|
|
|
361
360
|
React.useEffect(() => {
|
|
362
361
|
return () => {
|
|
363
|
-
|
|
362
|
+
isMounted.current = false;
|
|
364
363
|
};
|
|
365
364
|
}, []);
|
|
366
365
|
|
|
@@ -142,14 +142,12 @@ export const ScrollerList = <T extends Record<string, any>>(
|
|
|
142
142
|
currentState: GridLoaderStates<T>,
|
|
143
143
|
newState: Partial<GridLoaderStates<T>>
|
|
144
144
|
) => {
|
|
145
|
-
if (!currentState.mounted) return currentState;
|
|
146
145
|
return { ...currentState, ...newState };
|
|
147
146
|
},
|
|
148
147
|
{
|
|
149
148
|
autoLoad,
|
|
150
149
|
currentPage: 0,
|
|
151
150
|
hasNextPage: true,
|
|
152
|
-
mounted: true,
|
|
153
151
|
isNextPageLoading: false,
|
|
154
152
|
orderBy: defaultOrderBy,
|
|
155
153
|
orderByAsc: defaultOrderByAsc,
|
|
@@ -158,6 +156,7 @@ export const ScrollerList = <T extends Record<string, any>>(
|
|
|
158
156
|
selectedItems: []
|
|
159
157
|
}
|
|
160
158
|
);
|
|
159
|
+
const isMounted = React.useRef(true);
|
|
161
160
|
|
|
162
161
|
// Load data
|
|
163
162
|
const loadDataLocal = (pageAdd: number = 1) => {
|
|
@@ -180,7 +179,7 @@ export const ScrollerList = <T extends Record<string, any>>(
|
|
|
180
179
|
};
|
|
181
180
|
|
|
182
181
|
loadData(loadProps).then((result) => {
|
|
183
|
-
if (result == null || !
|
|
182
|
+
if (result == null || !isMounted.current) {
|
|
184
183
|
return;
|
|
185
184
|
}
|
|
186
185
|
|
|
@@ -302,7 +301,7 @@ export const ScrollerList = <T extends Record<string, any>>(
|
|
|
302
301
|
// Remove scroll event
|
|
303
302
|
window.removeEventListener('scroll', handleWindowScroll);
|
|
304
303
|
|
|
305
|
-
|
|
304
|
+
isMounted.current = false;
|
|
306
305
|
};
|
|
307
306
|
}, []);
|
|
308
307
|
|
package/src/mu/NotifierMU.tsx
CHANGED
|
@@ -248,7 +248,7 @@ export class NotificationMU extends NotificationReact {
|
|
|
248
248
|
}
|
|
249
249
|
result = this.onReturn(numberValue);
|
|
250
250
|
} else if (type === 'switch') {
|
|
251
|
-
const boolValue = inputRef.current.value
|
|
251
|
+
const boolValue = inputRef.current.value === 'true';
|
|
252
252
|
result = this.onReturn(boolValue);
|
|
253
253
|
} else {
|
|
254
254
|
const textValue = inputRef.current.value.trim();
|
package/src/mu/TableEx.tsx
CHANGED
|
@@ -163,6 +163,7 @@ export function TableEx<T extends Record<string, any>>(props: TableExProps<T>) {
|
|
|
163
163
|
selectedItems: []
|
|
164
164
|
}
|
|
165
165
|
);
|
|
166
|
+
const isMounted = React.useRef(true);
|
|
166
167
|
|
|
167
168
|
// Reset the state and load again
|
|
168
169
|
const reset = (add?: {}) => {
|
|
@@ -216,7 +217,7 @@ export function TableEx<T extends Record<string, any>>(props: TableExProps<T>) {
|
|
|
216
217
|
};
|
|
217
218
|
|
|
218
219
|
loadData(loadProps).then((result) => {
|
|
219
|
-
if (result == null) {
|
|
220
|
+
if (!isMounted.current || result == null) {
|
|
220
221
|
return;
|
|
221
222
|
}
|
|
222
223
|
|
|
@@ -325,6 +326,12 @@ export function TableEx<T extends Record<string, any>>(props: TableExProps<T>) {
|
|
|
325
326
|
if (currentPage === 0 && stateAutoLoad && lastLoadedItems == null)
|
|
326
327
|
loadDataLocal();
|
|
327
328
|
|
|
329
|
+
React.useEffect(() => {
|
|
330
|
+
return () => {
|
|
331
|
+
isMounted.current = false;
|
|
332
|
+
};
|
|
333
|
+
}, []);
|
|
334
|
+
|
|
328
335
|
// Layout
|
|
329
336
|
return (
|
|
330
337
|
<Paper>
|
package/src/mu/Tiplist.tsx
CHANGED
|
@@ -113,6 +113,7 @@ export function Tiplist<T extends Record<string, any>>(props: TiplistProps<T>) {
|
|
|
113
113
|
idLoaded?: boolean;
|
|
114
114
|
idSet?: boolean;
|
|
115
115
|
}>({});
|
|
116
|
+
const isMounted = React.useRef(true);
|
|
116
117
|
|
|
117
118
|
// Add readOnly
|
|
118
119
|
const addReadOnly = (params: AutocompleteRenderInputParams) => {
|
|
@@ -175,6 +176,8 @@ export function Tiplist<T extends Record<string, any>>(props: TiplistProps<T>) {
|
|
|
175
176
|
|
|
176
177
|
// Load list
|
|
177
178
|
loadData(keyword, id).then((options) => {
|
|
179
|
+
if (!isMounted.current) return;
|
|
180
|
+
|
|
178
181
|
// Indicates loading completed
|
|
179
182
|
stateUpdate({
|
|
180
183
|
loading: false,
|
|
@@ -213,6 +216,17 @@ export function Tiplist<T extends Record<string, any>>(props: TiplistProps<T>) {
|
|
|
213
216
|
}
|
|
214
217
|
}
|
|
215
218
|
|
|
219
|
+
React.useEffect(() => {
|
|
220
|
+
return () => {
|
|
221
|
+
// Clear any seed
|
|
222
|
+
if (state.loadDataSeed != null) {
|
|
223
|
+
clearTimeout(state.loadDataSeed);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
isMounted.current = false;
|
|
227
|
+
};
|
|
228
|
+
}, []);
|
|
229
|
+
|
|
216
230
|
// Layout
|
|
217
231
|
return (
|
|
218
232
|
<div>
|
|
@@ -35,6 +35,7 @@ export function CommonPage(props: CommonPageProps) {
|
|
|
35
35
|
moreActions,
|
|
36
36
|
onRefresh,
|
|
37
37
|
onUpdate,
|
|
38
|
+
onUpdateAll,
|
|
38
39
|
paddings = MUGlobal.pagePaddings,
|
|
39
40
|
scrollContainer,
|
|
40
41
|
pullContainer,
|
|
@@ -51,8 +52,12 @@ export function CommonPage(props: CommonPageProps) {
|
|
|
51
52
|
const labels = Labels.CommonPage;
|
|
52
53
|
|
|
53
54
|
// Update
|
|
54
|
-
const update =
|
|
55
|
-
?
|
|
55
|
+
const update = onUpdateAll
|
|
56
|
+
? onUpdateAll
|
|
57
|
+
: onUpdate
|
|
58
|
+
? (authorized?: boolean) => {
|
|
59
|
+
if (authorized == null || authorized) onUpdate();
|
|
60
|
+
}
|
|
56
61
|
: onRefresh
|
|
57
62
|
? (authorized?: boolean) => {
|
|
58
63
|
if (authorized) onRefresh();
|
|
@@ -24,14 +24,19 @@ export interface CommonPageProps extends Omit<ContainerProps, 'id'> {
|
|
|
24
24
|
moreActions?: MoreAction[];
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* On refresh callback
|
|
27
|
+
* On refresh callback, only when authorized = true
|
|
28
28
|
*/
|
|
29
29
|
onRefresh?: () => void | PromiseLike<void>;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* On page update, may uses onRefresh
|
|
32
|
+
* On page update, when authorized = null or true case, may uses onRefresh
|
|
33
33
|
*/
|
|
34
|
-
onUpdate?:
|
|
34
|
+
onUpdate?: () => void | PromiseLike<void>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* On page update, all cases with authorized
|
|
38
|
+
*/
|
|
39
|
+
onUpdateAll?: IStateUpdate;
|
|
35
40
|
|
|
36
41
|
/**
|
|
37
42
|
* Paddings
|