@cubejs-client/react 1.7.37 → 1.7.39

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cubejs-client/react",
3
- "version": "1.7.37",
3
+ "version": "1.7.39",
4
4
  "author": "Cube Dev, Inc.",
5
5
  "license": "MIT",
6
6
  "engines": {},
@@ -15,7 +15,10 @@
15
15
  "types": "dist/index.d.ts",
16
16
  "scripts": {
17
17
  "build": "tsc --build",
18
+ "test": "yarn unit",
19
+ "test:types": "tsc --project tsconfig.test.json",
18
20
  "tsc": "tsc --build",
21
+ "unit": "yarn test:types && jest --runInBand",
19
22
  "watch": "tsc --build --watch"
20
23
  },
21
24
  "files": [
@@ -24,18 +27,28 @@
24
27
  ],
25
28
  "dependencies": {
26
29
  "@babel/runtime": "^7.13.9",
27
- "@cubejs-client/core": "1.7.37",
30
+ "@cubejs-client/core": "1.7.39",
28
31
  "core-js": "^3.34.0",
29
32
  "ramda": "^0.27.2"
30
33
  },
31
34
  "devDependencies": {
32
35
  "@babel/core": "^7.24.5",
36
+ "@babel/preset-env": "^7.24.5",
37
+ "@babel/preset-react": "^7.12.13",
38
+ "@babel/preset-typescript": "^7.13.0",
39
+ "@types/jest": "^29",
33
40
  "@types/ramda": "^0.27.40",
34
- "@types/react": "^16.9.41",
41
+ "@types/react": "^18.3.0",
42
+ "@types/react-dom": "^18.3.0",
43
+ "babel-jest": "^29",
44
+ "jest": "^29",
45
+ "jest-environment-jsdom": "^29",
46
+ "react": "^18.3.1",
47
+ "react-dom": "^18.3.1",
35
48
  "typescript": "~6.0.3"
36
49
  },
37
50
  "peerDependencies": {
38
51
  "react": ">=16.10.2"
39
52
  },
40
- "gitHead": "28c8efd405d900a1b9cb6cb5ebc2ec951aa375be"
53
+ "gitHead": "1acb83d943046de04efa488429a8312d05ecf204"
41
54
  }
@@ -4,7 +4,7 @@ import CubeContext from './CubeContext';
4
4
  import type { CubeContextProps, CubeProviderProps } from './types';
5
5
 
6
6
  /**
7
- * Cube.js context provider
7
+ * Cube context provider
8
8
  * ```js
9
9
  * import React from 'react';
10
10
  * import cube from '@cubejs-client/core';
@@ -78,7 +78,7 @@ const granularities: GranularityOption[] = [
78
78
  ];
79
79
 
80
80
  /**
81
- * `<QueryBuilder />` is used to build interactive analytics query builders. It abstracts state management and API calls to Cube.js Backend. It uses render prop technique and doesn’t render anything itself, but calls the render function instead.
81
+ * `<QueryBuilder />` is used to build interactive analytics query builders. It abstracts state management and API calls to Cube Backend. It uses render prop technique and doesn’t render anything itself, but calls the render function instead.
82
82
  *
83
83
  * **Example**
84
84
  *
@@ -144,6 +144,8 @@ const granularities: GranularityOption[] = [
144
144
  export default class QueryBuilder extends React.Component<QueryBuilderProps, QueryBuilderInternalState> {
145
145
  static contextType = CubeContext;
146
146
 
147
+ context!: React.ContextType<typeof CubeContext>;
148
+
147
149
  static defaultProps = {
148
150
  cubeApi: null,
149
151
  stateChangeHeuristics: null,
@@ -278,10 +280,6 @@ export default class QueryBuilder extends React.Component<QueryBuilderProps, Que
278
280
  // TypeScript field declarations into `everything-else`, which the configured
279
281
  // order puts after `lifecycle`. Runtime is unaffected: field initializers run
280
282
  // right after `super()`, so the constructor's assignments still win.
281
- //
282
- // `this.context` is not re-declared: React types it as `any`, and a field
283
- // declaration would be emitted at runtime and shadow the context React
284
- // assigns.
285
283
  private mutexObj: MutexObj;
286
284
 
287
285
  private orderMembersOrderKeys: string[];
@@ -20,6 +20,8 @@ import type {
20
20
  export default class QueryRenderer extends React.Component<QueryRendererProps, QueryRendererState> {
21
21
  static contextType = CubeContext;
22
22
 
23
+ context!: React.ContextType<typeof CubeContext>;
24
+
23
25
  static defaultProps = {
24
26
  cubeApi: null,
25
27
  query: null,
@@ -83,10 +85,6 @@ export default class QueryRenderer extends React.Component<QueryRendererProps, Q
83
85
  // TypeScript field declarations into `everything-else`, which the configured
84
86
  // order puts after `lifecycle`. Runtime is unaffected: field initializers run
85
87
  // right after `super()`, so the constructor's assignments still win.
86
- //
87
- // `this.context` is not re-declared: React types it as `any`, and a field
88
- // declaration would be emitted at runtime and shadow the context React
89
- // assigns.
90
88
  private mutexObj: MutexObj;
91
89
 
92
90
  cubeApi(): CubeApi {
@@ -22,7 +22,7 @@ import type {
22
22
  } from '../types';
23
23
 
24
24
  /**
25
- * A React hook for executing Cube.js queries
25
+ * A React hook for executing Cube queries
26
26
  * ```js
27
27
  * import React from 'react';
28
28
  * import { Table } from 'antd';
@@ -75,15 +75,33 @@ export function useCubeQuery(
75
75
  const [progress, setProgress] = useState<ProgressResponse | null>(null);
76
76
  const [error, setError] = useState<Error | null>(null);
77
77
  const context = useContext(CubeContext);
78
+ const requestIdRef = useRef(0);
79
+ const hasStartedRequestRef = useRef(false);
78
80
 
79
81
  let subscribeRequest: UnsubscribeObj | null = null;
80
82
 
81
- // `progressResponse` is not part of the public `ProgressResult` API
82
- const progressCallback: ProgressCallback = (progressResult) => setProgress(
83
- (progressResult as unknown as ProgressResultWithResponse).progressResponse
84
- );
83
+ function isCurrentRequest(requestId: number) {
84
+ return requestId === requestIdRef.current;
85
+ }
85
86
 
86
- async function fetch() {
87
+ function startRequest() {
88
+ hasStartedRequestRef.current = true;
89
+
90
+ return ++requestIdRef.current;
91
+ }
92
+
93
+ function createProgressCallback(requestId: number): ProgressCallback {
94
+ return (progressResult) => {
95
+ if (isCurrentRequest(requestId)) {
96
+ // `progressResponse` is not part of the public `ProgressResult` API
97
+ setProgress(
98
+ (progressResult as unknown as ProgressResultWithResponse).progressResponse
99
+ );
100
+ }
101
+ };
102
+ }
103
+
104
+ async function fetchQuery(requestId: number) {
87
105
  const { resetResultSetOnChange } = options;
88
106
  const cubeApi = options.cubeApi || context?.cubeApi;
89
107
 
@@ -97,25 +115,38 @@ export function useCubeQuery(
97
115
 
98
116
  setError(null);
99
117
  setLoading(true);
118
+ setProgress(null);
100
119
 
101
120
  try {
102
121
  const response = await cubeApi.load(query, {
103
122
  mutexObj: mutexRef.current,
104
123
  mutexKey: 'query',
105
- progressCallback,
124
+ progressCallback: createProgressCallback(requestId),
106
125
  castNumerics: Boolean(typeof options.castNumerics === 'boolean' ? options.castNumerics : context?.options?.castNumerics),
107
126
  ...(options.cache ? { cache: options.cache } : {}),
108
127
  });
109
128
 
110
- setResultSet(response);
111
- setProgress(null);
129
+ if (isCurrentRequest(requestId)) {
130
+ setResultSet(response);
131
+ setProgress(null);
132
+ }
112
133
  } catch (loadError) {
113
- setError(loadError as Error);
114
- setResultSet(null);
115
- setProgress(null);
134
+ if (isCurrentRequest(requestId)) {
135
+ setError(loadError as Error);
136
+ setResultSet(null);
137
+ setProgress(null);
138
+ }
139
+ }
140
+
141
+ if (isCurrentRequest(requestId)) {
142
+ setLoading(false);
116
143
  }
144
+ }
145
+
146
+ async function fetch() {
147
+ const requestId = startRequest();
117
148
 
118
- setLoading(false);
149
+ await fetchQuery(requestId);
119
150
  }
120
151
 
121
152
  useEffect(() => {
@@ -129,6 +160,8 @@ export function useCubeQuery(
129
160
 
130
161
  async function loadQuery() {
131
162
  if (!skip && isQueryPresent(query)) {
163
+ const requestId = startRequest();
164
+
132
165
  // `areQueriesEqual` is declared for a single query, and reads no more
133
166
  // than `order` off one when given an array of queries
134
167
  const previousQuery = currentQuery as DeeplyReadonly<Query> | null;
@@ -142,6 +175,7 @@ export function useCubeQuery(
142
175
 
143
176
  setError(null);
144
177
  setLoading(true);
178
+ setProgress(null);
145
179
 
146
180
  try {
147
181
  if (subscribeRequest) {
@@ -155,34 +189,43 @@ export function useCubeQuery(
155
189
  {
156
190
  mutexObj: mutexRef.current,
157
191
  mutexKey: 'query',
158
- progressCallback,
192
+ progressCallback: createProgressCallback(requestId),
159
193
  ...(options.cache ? { cache: options.cache } : {}),
160
194
  },
161
195
  (e, result) => {
162
- if (e) {
163
- setError(e);
164
- } else {
165
- setResultSet(result);
196
+ if (isCurrentRequest(requestId)) {
197
+ if (e) {
198
+ setError(e);
199
+ } else {
200
+ setResultSet(result);
201
+ }
202
+ setLoading(false);
203
+ setProgress(null);
166
204
  }
167
- setLoading(false);
168
- setProgress(null);
169
205
  }
170
206
  );
171
207
  } else {
172
- await fetch();
208
+ await fetchQuery(requestId);
173
209
  }
174
210
  } catch (e) {
175
- setError(e as Error);
176
- setResultSet(null);
177
- setLoading(false);
178
- setProgress(null);
211
+ if (isCurrentRequest(requestId)) {
212
+ setError(e as Error);
213
+ setResultSet(null);
214
+ setLoading(false);
215
+ setProgress(null);
216
+ }
179
217
  }
218
+ } else if (hasStartedRequestRef.current) {
219
+ setLoading(false);
220
+ setProgress(null);
180
221
  }
181
222
  }
182
223
 
183
224
  loadQuery();
184
225
 
185
226
  return () => {
227
+ requestIdRef.current += 1;
228
+
186
229
  if (subscribeRequest) {
187
230
  subscribeRequest.unsubscribe();
188
231
  subscribeRequest = null;
package/src/index.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * @menuCategory Reference
5
5
  * @subcategory Frontend
6
6
  * @menuOrder 3
7
- * @description `@cubejs-client/react` provides React Components for easy Cube.js integration in a React app.
7
+ * @description `@cubejs-client/react` provides React Components for easy Cube integration in a React app.
8
8
  */
9
9
  import QueryRenderer from './QueryRenderer';
10
10
  import QueryRendererWithTotals from './QueryRendererWithTotals';
package/src/types.ts CHANGED
@@ -86,7 +86,7 @@ export type QueryRendererProps = {
86
86
  query: Query | Query[];
87
87
  queries?: { [key: string]: Query };
88
88
  /**
89
- * Indicates whether the generated by `Cube.js` SQL Code should be requested. See [rest-api#sql](/reference/rest-api#v1sql). When set to `only` then only the request to [/v1/sql](/reference/rest-api#v1sql) will be performed. When set to `true` the sql request will be performed along with the query request. Will not be performed if set to `false`
89
+ * Indicates whether the generated by `Cube` SQL Code should be requested. See [rest-api#sql](/reference/rest-api#v1sql). When set to `only` then only the request to [/v1/sql](/reference/rest-api#v1sql) will be performed. When set to `true` the sql request will be performed along with the query request. Will not be performed if set to `false`
90
90
  */
91
91
  loadSql?: 'only' | boolean;
92
92
  /**
@@ -245,19 +245,19 @@ export type QueryBuilderRenderProps = {
245
245
 
246
246
  availableFilterMembers: Array<AvailableCube<TCubeMeasure> | AvailableCube<TCubeDimension>>;
247
247
  /**
248
- * An array of available measures to select. They are loaded via the API from Cube.js Backend.
248
+ * An array of available measures to select. They are loaded via the API from Cube Backend.
249
249
  */
250
250
  availableMeasures: TCubeMeasure[];
251
251
  /**
252
- * An array of available dimensions to select. They are loaded via the API from Cube.js Backend.
252
+ * An array of available dimensions to select. They are loaded via the API from Cube Backend.
253
253
  */
254
254
  availableDimensions: TCubeDimension[];
255
255
  /**
256
- * An array of available time dimensions to select. They are loaded via the API from Cube.js Backend.
256
+ * An array of available time dimensions to select. They are loaded via the API from Cube Backend.
257
257
  */
258
258
  availableTimeDimensions: TCubeDimension[];
259
259
  /**
260
- * An array of available segments to select. They are loaded via the API from Cube.js Backend.
260
+ * An array of available segments to select. They are loaded via the API from Cube Backend.
261
261
  */
262
262
  availableSegments: TCubeSegment[];
263
263