Skip to content

Commit 008cb60

Browse files
committed
Fix initial loading of libraries.
1 parent 2cdd635 commit 008cb60

File tree

4 files changed

+125
-38
lines changed

4 files changed

+125
-38
lines changed

dash/dash-renderer/src/APIController.react.js

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import {batch, connect} from 'react-redux';
22
import {includes, isEmpty} from 'ramda';
3-
import React, {useEffect, useRef, useState, createContext} from 'react';
3+
import React, {
4+
useEffect,
5+
useRef,
6+
useState,
7+
createContext,
8+
useCallback
9+
} from 'react';
410
import PropTypes from 'prop-types';
511
import TreeContainer from './TreeContainer';
612
import GlobalErrorContainer from './components/error/GlobalErrorContainer.react';
@@ -47,6 +53,10 @@ const UnconnectedContainer = props => {
4753
if (!events.current) {
4854
events.current = new EventEmitter();
4955
}
56+
57+
const [libraryReady, setLibraryReady] = useState(false);
58+
const onLibraryReady = useCallback(() => setLibraryReady(true), []);
59+
5060
const renderedTree = useRef(false);
5161

5262
const propsRef = useRef({});
@@ -61,7 +71,9 @@ const UnconnectedContainer = props => {
6171
})
6272
});
6373

64-
useEffect(storeEffect.bind(null, props, events, setErrorLoading));
74+
useEffect(
75+
storeEffect.bind(null, props, events, setErrorLoading, libraryReady)
76+
);
6577

6678
useEffect(() => {
6779
if (renderedTree.current) {
@@ -98,38 +110,43 @@ const UnconnectedContainer = props => {
98110

99111
content = (
100112
<DashContext.Provider value={provider.current}>
101-
<LibraryManager
102-
requests_pathname_prefix={config.requests_pathname_prefix}
103-
>
104-
<TreeContainer
105-
_dashprivate_error={error}
106-
_dashprivate_layout={layout}
107-
_dashprivate_loadingState={getLoadingState(
108-
layout,
109-
[],
110-
loadingMap
111-
)}
112-
_dashprivate_loadingStateHash={getLoadingHash(
113-
[],
114-
loadingMap
115-
)}
116-
_dashprivate_path={JSON.stringify([])}
117-
/>
118-
</LibraryManager>
113+
<TreeContainer
114+
_dashprivate_error={error}
115+
_dashprivate_layout={layout}
116+
_dashprivate_loadingState={getLoadingState(
117+
layout,
118+
[],
119+
loadingMap
120+
)}
121+
_dashprivate_loadingStateHash={getLoadingHash(
122+
[],
123+
loadingMap
124+
)}
125+
_dashprivate_path={JSON.stringify([])}
126+
/>
119127
</DashContext.Provider>
120128
);
121129
} else {
122130
content = <div className='_dash-loading'>Loading...</div>;
123131
}
124132

125-
return config && config.ui === true ? (
126-
<GlobalErrorContainer>{content}</GlobalErrorContainer>
127-
) : (
128-
content
133+
return (
134+
<LibraryManager
135+
requests_pathname_prefix={config.requests_pathname_prefix}
136+
onReady={onLibraryReady}
137+
ready={libraryReady}
138+
layout={layoutRequest && layoutRequest.content}
139+
>
140+
{config && config.ui === true ? (
141+
<GlobalErrorContainer>{content}</GlobalErrorContainer>
142+
) : (
143+
content
144+
)}
145+
</LibraryManager>
129146
);
130147
};
131148

132-
function storeEffect(props, events, setErrorLoading) {
149+
function storeEffect(props, events, setErrorLoading, libraryReady) {
133150
const {
134151
appLifecycle,
135152
dependenciesRequest,
@@ -148,7 +165,7 @@ function storeEffect(props, events, setErrorLoading) {
148165
}
149166
dispatch(apiThunk('_dash-layout', 'GET', 'layoutRequest'));
150167
} else if (layoutRequest.status === STATUS.OK) {
151-
if (isEmpty(layout)) {
168+
if (isEmpty(layout) && libraryReady) {
152169
if (typeof hooks.layout_post === 'function') {
153170
hooks.layout_post(layoutRequest.content);
154171
}
@@ -191,7 +208,8 @@ function storeEffect(props, events, setErrorLoading) {
191208
layoutRequest.status === STATUS.OK &&
192209
!isEmpty(layout) &&
193210
// Hasn't already hydrated
194-
appLifecycle === getAppState('STARTED')
211+
appLifecycle === getAppState('STARTED') &&
212+
libraryReady
195213
) {
196214
let hasError = false;
197215
try {
@@ -240,7 +258,8 @@ const Container = connect(
240258
graphs: state.graphs,
241259
history: state.history,
242260
error: state.error,
243-
config: state.config
261+
config: state.config,
262+
paths: state.paths
244263
}),
245264
dispatch => ({dispatch})
246265
)(UnconnectedContainer);
Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,67 @@
1-
import React, {JSX} from 'react';
1+
import React, {JSX, useEffect, useState} from 'react';
22

33
import {createLibrariesContext, LibrariesContext} from './librariesContext';
4+
import {crawlLayout} from '../actions/utils';
5+
import {isEmpty} from 'ramda';
46

57
type LibrariesManagerProps = {
68
children: JSX.Element;
79
requests_pathname_prefix: string;
10+
onReady: () => void;
11+
ready: boolean;
12+
layout?: any;
13+
initialLibraries?: string[];
814
};
915

10-
const LibraryManager = (props: LibrariesManagerProps) => {
11-
const {children, requests_pathname_prefix} = props;
12-
const contextValue = createLibrariesContext(requests_pathname_prefix);
13-
16+
const LibraryProvider = (props: LibrariesManagerProps) => {
17+
const {
18+
children,
19+
requests_pathname_prefix,
20+
onReady,
21+
ready,
22+
initialLibraries
23+
} = props;
24+
const contextValue = createLibrariesContext(
25+
requests_pathname_prefix,
26+
initialLibraries as string[],
27+
onReady,
28+
ready
29+
);
1430
return (
1531
<LibrariesContext.Provider value={contextValue}>
1632
{children}
1733
</LibrariesContext.Provider>
1834
);
1935
};
2036

37+
const LibraryManager = (props: LibrariesManagerProps) => {
38+
const {children, ready, layout} = props;
39+
40+
const [initialLibraries, setInitialLibraries] = useState<string[] | null>(
41+
null
42+
);
43+
44+
useEffect(() => {
45+
if (layout && !isEmpty(layout) && !ready && !initialLibraries) {
46+
const libraries: string[] = [];
47+
crawlLayout(layout, (child: any) => {
48+
if (child.namespace && !libraries.includes(child.namespace)) {
49+
libraries.push(child.namespace);
50+
}
51+
});
52+
setInitialLibraries(libraries);
53+
}
54+
}, [layout, ready, initialLibraries]);
55+
56+
if (!initialLibraries) {
57+
return children;
58+
}
59+
60+
return (
61+
<LibraryProvider {...props} initialLibraries={initialLibraries}>
62+
{children}
63+
</LibraryProvider>
64+
);
65+
};
66+
2167
export default LibraryManager;

dash/dash-renderer/src/libraries/librariesContext.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,24 @@ export function librariesReducer(
101101
}
102102

103103
export function createLibrariesContext(
104-
pathnamePrefix: string
104+
pathnamePrefix: string,
105+
initialLibraries: string[],
106+
onReady: () => void,
107+
ready: boolean
105108
): LibrariesContextType {
106-
const [state, dispatch] = useReducer(librariesReducer, {});
109+
const [state, dispatch] = useReducer(librariesReducer, {}, () => {
110+
const libState: LibrariesState = {};
111+
initialLibraries.forEach(lib => {
112+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
113+
// @ts-ignore
114+
if (window[lib]) {
115+
libState[lib] = {toLoad: false, loaded: true, loading: false};
116+
} else {
117+
libState[lib] = {toLoad: true, loaded: false, loading: false};
118+
}
119+
});
120+
return libState;
121+
});
107122
const [callback, setCallback] = useState<number>(-1);
108123
const createAction = (type: LibrariesActions) => (payload: any) =>
109124
dispatch({type, payload});
@@ -191,13 +206,17 @@ export function createLibrariesContext(
191206
.then(() => {
192207
setLoaded({libraries});
193208
setCallback(-1);
209+
onReady();
194210
});
195211
};
196212

197213
// Load libraries on a throttle to have time to gather all the components in one go.
198214
useEffect(() => {
199215
const libraries = getLibrariesToLoad();
200216
if (!libraries.length) {
217+
if (!ready && initialLibraries.length === 0) {
218+
onReady();
219+
}
201220
return;
202221
}
203222
if (callback > 0) {

dash/dash-renderer/src/persistence.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ import {
6868
} from 'ramda';
6969
import {createAction} from 'redux-actions';
7070

71-
// import Registry from './registry';
71+
import Registry from './registry';
7272
import {stringifyId} from './actions/dependencies';
7373

7474
export const storePrefix = '_dash_persistence.';
@@ -289,7 +289,8 @@ const getProps = layout => {
289289
}
290290
const {id, persistence} = props;
291291

292-
const getVal = prop => props[prop] || {}[prop];
292+
const element = Registry.resolve(layout);
293+
const getVal = prop => props[prop] || (element.defaultProps || {})[prop];
293294
const persisted_props = getVal('persisted_props');
294295
const persistence_type = getVal('persistence_type');
295296
const canPersist = id && persisted_props && persistence_type;
@@ -298,6 +299,7 @@ const getProps = layout => {
298299
canPersist,
299300
id,
300301
props,
302+
element,
301303
persistence,
302304
persisted_props,
303305
persistence_type
@@ -309,6 +311,7 @@ export function recordUiEdit(layout, newProps, dispatch) {
309311
canPersist,
310312
id,
311313
props,
314+
element,
312315
persistence,
313316
persisted_props,
314317
persistence_type
@@ -321,7 +324,7 @@ export function recordUiEdit(layout, newProps, dispatch) {
321324
const [propName, propPart] = persistedProp.split('.');
322325
if (newProps[propName] !== undefined) {
323326
const storage = getStore(persistence_type, dispatch);
324-
const {extract} = getTransform({}, propName, propPart);
327+
const {extract} = getTransform(element, propName, propPart);
325328

326329
const valsKey = getValsKey(id, persistedProp, persistence);
327330
let originalVal = extract(props[propName]);

0 commit comments

Comments
 (0)