@cubejs-client/react 1.7.13 → 1.7.14
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/dist/CubeContext.d.ts +35 -0
- package/dist/CubeContext.d.ts.map +1 -0
- package/dist/CubeProvider.d.ts +31 -0
- package/dist/CubeProvider.d.ts.map +1 -0
- package/dist/QueryBuilder.d.ts +135 -0
- package/dist/QueryBuilder.d.ts.map +1 -0
- package/dist/QueryRenderer.d.ts +34 -0
- package/dist/QueryRenderer.d.ts.map +1 -0
- package/dist/QueryRendererWithTotals.d.ts +4 -0
- package/dist/QueryRendererWithTotals.d.ts.map +1 -0
- package/dist/cubejs-client-react.cjs.js +248 -23
- package/dist/cubejs-client-react.cjs.js.map +1 -1
- package/dist/cubejs-client-react.esm.js +253 -31
- package/dist/cubejs-client-react.esm.js.map +1 -1
- package/dist/cubejs-client-react.umd.js +248 -23
- package/dist/cubejs-client-react.umd.js.map +1 -1
- package/dist/hooks/cube-fetch.d.ts +6 -0
- package/dist/hooks/cube-fetch.d.ts.map +1 -0
- package/dist/hooks/cube-meta.d.ts +4 -0
- package/dist/hooks/cube-meta.d.ts.map +1 -0
- package/dist/hooks/cube-query.d.ts +39 -0
- package/dist/hooks/cube-query.d.ts.map +1 -0
- package/dist/hooks/cube-sql.d.ts +12 -0
- package/dist/hooks/cube-sql.d.ts.map +1 -0
- package/dist/hooks/deep-compare-memoize.d.ts +2 -0
- package/dist/hooks/deep-compare-memoize.d.ts.map +1 -0
- package/dist/hooks/dry-run.d.ts +7 -0
- package/dist/hooks/dry-run.d.ts.map +1 -0
- package/dist/hooks/lazy-dry-run.d.ts +7 -0
- package/dist/hooks/lazy-dry-run.d.ts.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/types.d.ts +765 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +14 -10
- package/src/CubeContext.ts +37 -0
- package/src/CubeProvider.tsx +42 -0
- package/src/{QueryBuilder.jsx → QueryBuilder.tsx} +238 -89
- package/src/{QueryRenderer.jsx → QueryRenderer.tsx} +39 -14
- package/src/QueryRendererWithTotals.tsx +24 -0
- package/src/hooks/cube-fetch.ts +98 -0
- package/src/hooks/cube-meta.ts +8 -0
- package/src/hooks/{cube-query.js → cube-query.ts} +90 -14
- package/src/hooks/cube-sql.ts +22 -0
- package/src/hooks/{deep-compare-memoize.js → deep-compare-memoize.ts} +2 -2
- package/src/hooks/dry-run.ts +17 -0
- package/src/hooks/lazy-dry-run.ts +24 -0
- package/src/index.ts +77 -0
- package/src/types.ts +837 -0
- package/src/{utils.js → utils.ts} +3 -3
- package/index.d.ts +0 -697
- package/src/CubeContext.js +0 -3
- package/src/CubeProvider.jsx +0 -14
- package/src/QueryRendererWithTotals.jsx +0 -20
- package/src/hooks/cube-fetch.js +0 -66
- package/src/hooks/cube-meta.js +0 -5
- package/src/hooks/cube-sql.js +0 -8
- package/src/hooks/dry-run.js +0 -8
- package/src/hooks/lazy-dry-run.js +0 -11
- package/src/index.js +0 -18
|
@@ -2,8 +2,45 @@ import React, { createContext, useRef, useContext, useState, useEffect } from 'r
|
|
|
2
2
|
import { equals, toPairs, fromPairs, uniqBy, prop, indexBy, uniq, pick, clone } from 'ramda';
|
|
3
3
|
import { isQueryPresent, getQueryMembers, flattenFilters, moveItemInArray, movePivotItem, removeEmptyQueryFields, defaultOrder, ResultSet, validateQuery, defaultHeuristics, areQueriesEqual } from '@cubejs-client/core';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* In case when you need direct access to `cubeApi` you can use `CubeContext` anywhere in your app
|
|
7
|
+
*
|
|
8
|
+
* ```js
|
|
9
|
+
* import React from 'react';
|
|
10
|
+
* import { CubeContext } from '@cubejs-client/react';
|
|
11
|
+
*
|
|
12
|
+
* export default function DisplayComponent() {
|
|
13
|
+
* const { cubeApi } = React.useContext(CubeContext);
|
|
14
|
+
* const [rawResults, setRawResults] = React.useState([]);
|
|
15
|
+
* const query = {
|
|
16
|
+
* ...
|
|
17
|
+
* };
|
|
18
|
+
*
|
|
19
|
+
* React.useEffect(() => {
|
|
20
|
+
* cubeApi.load(query).then((resultSet) => {
|
|
21
|
+
* setRawResults(resultSet.rawData());
|
|
22
|
+
* });
|
|
23
|
+
* }, [query]);
|
|
24
|
+
*
|
|
25
|
+
* return (
|
|
26
|
+
* <>
|
|
27
|
+
* {rawResults.map(row => (
|
|
28
|
+
* ...
|
|
29
|
+
* ))}
|
|
30
|
+
* </>
|
|
31
|
+
* )
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
// The context has no default value: `cubeApi` is only available under a
|
|
36
|
+
// `CubeProvider`, and consumers guard against a missing context.
|
|
5
37
|
var CubeContext = /*#__PURE__*/createContext(null);
|
|
6
38
|
|
|
39
|
+
/**
|
|
40
|
+
* `<QueryRenderer />` a react component that accepts a query, fetches the given query, and uses the render prop to render the resulting data
|
|
41
|
+
* @stickyTypes QueryRendererProps, QueryRendererRenderProps
|
|
42
|
+
* @noInheritDoc
|
|
43
|
+
*/
|
|
7
44
|
class QueryRenderer extends React.Component {
|
|
8
45
|
static contextType = CubeContext;
|
|
9
46
|
static defaultProps = {
|
|
@@ -64,6 +101,16 @@ class QueryRenderer extends React.Component {
|
|
|
64
101
|
this.loadQueries(queries);
|
|
65
102
|
}
|
|
66
103
|
}
|
|
104
|
+
|
|
105
|
+
// These sit after the lifecycle methods because `react/sort-comp` sorts
|
|
106
|
+
// TypeScript field declarations into `everything-else`, which the configured
|
|
107
|
+
// order puts after `lifecycle`. Runtime is unaffected: field initializers run
|
|
108
|
+
// right after `super()`, so the constructor's assignments still win.
|
|
109
|
+
//
|
|
110
|
+
// `this.context` is not re-declared: React types it as `any`, and a field
|
|
111
|
+
// declaration would be emitted at runtime and shadow the context React
|
|
112
|
+
// assigns.
|
|
113
|
+
|
|
67
114
|
cubeApi() {
|
|
68
115
|
// eslint-disable-next-line react/destructuring-assignment
|
|
69
116
|
return this.props.cubeApi || this.context && this.context.cubeApi;
|
|
@@ -191,6 +238,8 @@ class QueryRenderer extends React.Component {
|
|
|
191
238
|
sqlQuery
|
|
192
239
|
};
|
|
193
240
|
if (render) {
|
|
241
|
+
// The prop is declared as returning `void` for backwards compatibility,
|
|
242
|
+
// while what it returns is what gets rendered
|
|
194
243
|
return render(loadState);
|
|
195
244
|
}
|
|
196
245
|
return null;
|
|
@@ -212,6 +261,7 @@ const QueryRendererWithTotals = ({
|
|
|
212
261
|
...restProps
|
|
213
262
|
}) => /*#__PURE__*/React.createElement(QueryRenderer, _extends({
|
|
214
263
|
queries: {
|
|
264
|
+
// `granularity: null` is sent to the API on purpose to get totals
|
|
215
265
|
totals: {
|
|
216
266
|
...query,
|
|
217
267
|
dimensions: [],
|
|
@@ -264,6 +314,71 @@ const granularities = [{
|
|
|
264
314
|
name: 'year',
|
|
265
315
|
title: 'Year'
|
|
266
316
|
}];
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* `<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.
|
|
320
|
+
*
|
|
321
|
+
* **Example**
|
|
322
|
+
*
|
|
323
|
+
* [Open in CodeSandbox](https://codesandbox.io/s/z6r7qj8wm)
|
|
324
|
+
* ```js
|
|
325
|
+
* import React from 'react';
|
|
326
|
+
* import ReactDOM from 'react-dom';
|
|
327
|
+
* import { Layout, Divider, Empty, Select } from 'antd';
|
|
328
|
+
* import { QueryBuilder } from '@cubejs-client/react';
|
|
329
|
+
* import cube from '@cubejs-client/core';
|
|
330
|
+
* import 'antd/dist/antd.css';
|
|
331
|
+
*
|
|
332
|
+
* import ChartRenderer from './ChartRenderer';
|
|
333
|
+
*
|
|
334
|
+
* const cubeApi = cube('YOUR-CUBE-API-TOKEN', {
|
|
335
|
+
* apiUrl: 'http://localhost:4000/cubejs-api/v1',
|
|
336
|
+
* });
|
|
337
|
+
*
|
|
338
|
+
* const App = () => (
|
|
339
|
+
* <QueryBuilder
|
|
340
|
+
* query={{
|
|
341
|
+
* timeDimensions: [
|
|
342
|
+
* {
|
|
343
|
+
* dimension: 'LineItems.createdAt',
|
|
344
|
+
* granularity: 'month',
|
|
345
|
+
* },
|
|
346
|
+
* ],
|
|
347
|
+
* }}
|
|
348
|
+
* cubeApi={cubeApi}
|
|
349
|
+
* render={({ resultSet, measures, availableMeasures, updateMeasures }) => (
|
|
350
|
+
* <Layout.Content style={{ padding: '20px' }}>
|
|
351
|
+
* <Select
|
|
352
|
+
* mode="multiple"
|
|
353
|
+
* style={{ width: '100%' }}
|
|
354
|
+
* placeholder="Please select"
|
|
355
|
+
* onSelect={(measure) => updateMeasures.add(measure)}
|
|
356
|
+
* onDeselect={(measure) => updateMeasures.remove(measure)}
|
|
357
|
+
* >
|
|
358
|
+
* {availableMeasures.map((measure) => (
|
|
359
|
+
* <Select.Option key={measure.name} value={measure}>
|
|
360
|
+
* {measure.title}
|
|
361
|
+
* </Select.Option>
|
|
362
|
+
* ))}
|
|
363
|
+
* </Select>
|
|
364
|
+
* <Divider />
|
|
365
|
+
* {measures.length > 0 ? (
|
|
366
|
+
* <ChartRenderer resultSet={resultSet} />
|
|
367
|
+
* ) : (
|
|
368
|
+
* <Empty description="Select measure or dimension to get started" />
|
|
369
|
+
* )}
|
|
370
|
+
* </Layout.Content>
|
|
371
|
+
* )}
|
|
372
|
+
* />
|
|
373
|
+
* );
|
|
374
|
+
*
|
|
375
|
+
* const rootElement = document.getElementById("root");
|
|
376
|
+
* ReactDOM.render(<App />, rootElement);
|
|
377
|
+
* ```
|
|
378
|
+
* @stickyTypes QueryBuilderProps, QueryBuilderRenderProps, QueryBuilderState
|
|
379
|
+
* @noInheritDoc
|
|
380
|
+
* @order 2
|
|
381
|
+
*/
|
|
267
382
|
class QueryBuilder extends React.Component {
|
|
268
383
|
static contextType = CubeContext;
|
|
269
384
|
static defaultProps = {
|
|
@@ -371,7 +486,7 @@ class QueryBuilder extends React.Component {
|
|
|
371
486
|
const newMeta = await this.cubeApi().meta();
|
|
372
487
|
if (!equals(newMeta, meta) && typeof onSchemaChange === 'function') {
|
|
373
488
|
onSchemaChange({
|
|
374
|
-
schemaVersion,
|
|
489
|
+
schemaVersion: schemaVersion,
|
|
375
490
|
refresh: async () => {
|
|
376
491
|
await this.fetchMeta();
|
|
377
492
|
}
|
|
@@ -385,6 +500,16 @@ class QueryBuilder extends React.Component {
|
|
|
385
500
|
}
|
|
386
501
|
}
|
|
387
502
|
}
|
|
503
|
+
|
|
504
|
+
// These sit after the lifecycle methods because `react/sort-comp` sorts
|
|
505
|
+
// TypeScript field declarations into `everything-else`, which the configured
|
|
506
|
+
// order puts after `lifecycle`. Runtime is unaffected: field initializers run
|
|
507
|
+
// right after `super()`, so the constructor's assignments still win.
|
|
508
|
+
//
|
|
509
|
+
// `this.context` is not re-declared: React types it as `any`, and a field
|
|
510
|
+
// declaration would be emitted at runtime and shadow the context React
|
|
511
|
+
// assigns.
|
|
512
|
+
|
|
388
513
|
fetchMeta = async () => {
|
|
389
514
|
if (!this.cubeApi()) {
|
|
390
515
|
return;
|
|
@@ -399,9 +524,10 @@ class QueryBuilder extends React.Component {
|
|
|
399
524
|
});
|
|
400
525
|
meta = await this.cubeApi().meta();
|
|
401
526
|
} catch (error) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
527
|
+
const requestError = error;
|
|
528
|
+
metaError = requestError.response?.plainError || requestError;
|
|
529
|
+
richMetaError = requestError;
|
|
530
|
+
metaErrorStack = requestError.response?.stack?.replace(requestError.message || '', '') || '';
|
|
405
531
|
}
|
|
406
532
|
this.setState({
|
|
407
533
|
meta,
|
|
@@ -537,6 +663,9 @@ class QueryBuilder extends React.Component {
|
|
|
537
663
|
const indexedMeasures = indexBy(prop('cubeName'), availableMembers.measures);
|
|
538
664
|
const indexedDimensions = indexBy(prop('cubeName'), availableMembers.dimensions);
|
|
539
665
|
const cubeNames = uniq([...Object.keys(indexedMeasures), ...Object.keys(indexedDimensions)]).sort();
|
|
666
|
+
|
|
667
|
+
// Measures and dimensions of a cube are merged into one member list, which
|
|
668
|
+
// the declared type describes as either one or the other
|
|
540
669
|
availableFilterMembers = cubeNames.map(name => {
|
|
541
670
|
const cube = indexedMeasures[name] || indexedDimensions[name];
|
|
542
671
|
return {
|
|
@@ -577,12 +706,14 @@ class QueryBuilder extends React.Component {
|
|
|
577
706
|
query,
|
|
578
707
|
error: queryError,
|
|
579
708
|
// Match same name as QueryRenderer prop
|
|
580
|
-
validatedQuery,
|
|
709
|
+
validatedQuery: validatedQuery,
|
|
581
710
|
isQueryPresent: this.isQueryPresent(),
|
|
582
711
|
chartType,
|
|
583
712
|
measures,
|
|
584
713
|
dimensions,
|
|
585
|
-
|
|
714
|
+
// The declared granularity options require a `name`, which the
|
|
715
|
+
// "w/o grouping" option leaves out
|
|
716
|
+
timeDimensions: timeDimensions,
|
|
586
717
|
segments,
|
|
587
718
|
filters,
|
|
588
719
|
orderMembers,
|
|
@@ -667,7 +798,7 @@ class QueryBuilder extends React.Component {
|
|
|
667
798
|
missingMembers,
|
|
668
799
|
refresh: this.fetchMeta,
|
|
669
800
|
isFetchingMeta,
|
|
670
|
-
dryRunResponse,
|
|
801
|
+
dryRunResponse: dryRunResponse,
|
|
671
802
|
...queryRendererProps
|
|
672
803
|
};
|
|
673
804
|
}
|
|
@@ -767,9 +898,10 @@ class QueryBuilder extends React.Component {
|
|
|
767
898
|
});
|
|
768
899
|
}
|
|
769
900
|
} catch (error) {
|
|
901
|
+
const requestError = error;
|
|
770
902
|
this.setState({
|
|
771
|
-
queryError: new Error(
|
|
772
|
-
richQueryError: new Error(
|
|
903
|
+
queryError: new Error(requestError.response?.plainError || requestError.message),
|
|
904
|
+
richQueryError: new Error(requestError.message || requestError.toString())
|
|
773
905
|
});
|
|
774
906
|
}
|
|
775
907
|
}
|
|
@@ -788,7 +920,7 @@ class QueryBuilder extends React.Component {
|
|
|
788
920
|
meta
|
|
789
921
|
} = this.state;
|
|
790
922
|
return defaultHeuristics(newState, query, {
|
|
791
|
-
meta,
|
|
923
|
+
meta: meta,
|
|
792
924
|
sessionGranularity: sessionGranularity || 'day'
|
|
793
925
|
});
|
|
794
926
|
}
|
|
@@ -832,18 +964,42 @@ class QueryBuilder extends React.Component {
|
|
|
832
964
|
}
|
|
833
965
|
}
|
|
834
966
|
|
|
835
|
-
|
|
967
|
+
/**
|
|
968
|
+
* Cube.js context provider
|
|
969
|
+
* ```js
|
|
970
|
+
* import React from 'react';
|
|
971
|
+
* import cube from '@cubejs-client/core';
|
|
972
|
+
* import { CubeProvider } from '@cubejs-client/react';
|
|
973
|
+
*
|
|
974
|
+
* const API_URL = 'https://harsh-eel.aws-us-east-2.cubecloudapp.dev';
|
|
975
|
+
* const CUBE_TOKEN =
|
|
976
|
+
* 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.* eyJpYXQiOjE1OTE3MDcxNDgsImV4cCI6MTU5NDI5OTE0OH0.* n5jGLQJ14igg6_Hri_Autx9qOIzVqp4oYxmX27V-4T4';
|
|
977
|
+
*
|
|
978
|
+
* const cubeApi = cube(CUBE_TOKEN, {
|
|
979
|
+
* apiUrl: `${API_URL}/cubejs-api/v1`,
|
|
980
|
+
* });
|
|
981
|
+
*
|
|
982
|
+
* export default function App() {
|
|
983
|
+
* return (
|
|
984
|
+
* <CubeProvider cubeApi={cubeApi}>
|
|
985
|
+
* //...
|
|
986
|
+
* </CubeProvider>
|
|
987
|
+
* )
|
|
988
|
+
* }
|
|
989
|
+
* ```
|
|
990
|
+
* @stickyTypes
|
|
991
|
+
* @order 10
|
|
992
|
+
*/
|
|
993
|
+
const CubeProvider = ({
|
|
836
994
|
cubeApi,
|
|
837
995
|
children,
|
|
838
996
|
options = {}
|
|
839
|
-
}) {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
}, children);
|
|
846
|
-
}
|
|
997
|
+
}) => /*#__PURE__*/React.createElement(CubeContext.Provider, {
|
|
998
|
+
value: {
|
|
999
|
+
cubeApi,
|
|
1000
|
+
options
|
|
1001
|
+
}
|
|
1002
|
+
}, children);
|
|
847
1003
|
|
|
848
1004
|
function useDeepCompareMemoize(value) {
|
|
849
1005
|
const ref = useRef([]);
|
|
@@ -853,6 +1009,10 @@ function useDeepCompareMemoize(value) {
|
|
|
853
1009
|
return ref.current;
|
|
854
1010
|
}
|
|
855
1011
|
|
|
1012
|
+
/**
|
|
1013
|
+
* @hidden
|
|
1014
|
+
*/
|
|
1015
|
+
|
|
856
1016
|
function useCubeFetch(method, options = {}) {
|
|
857
1017
|
const context = useContext(CubeContext);
|
|
858
1018
|
const mutexRef = useRef({});
|
|
@@ -867,6 +1027,7 @@ function useCubeFetch(method, options = {}) {
|
|
|
867
1027
|
async function load(loadOptions = {}, ignoreSkip = false) {
|
|
868
1028
|
const cubeApi = options.cubeApi || context?.cubeApi;
|
|
869
1029
|
const query = loadOptions.query || options.query;
|
|
1030
|
+
const onlyViews = 'onlyViews' in loadOptions ? loadOptions.onlyViews : options.onlyViews;
|
|
870
1031
|
const queryCondition = method === 'meta' ? true : query && isQueryPresent(query);
|
|
871
1032
|
if (cubeApi && (ignoreSkip || !skip) && queryCondition) {
|
|
872
1033
|
setError(null);
|
|
@@ -879,17 +1040,23 @@ function useCubeFetch(method, options = {}) {
|
|
|
879
1040
|
mutexKey: method,
|
|
880
1041
|
...(options.baseRequestId ? {
|
|
881
1042
|
baseRequestId: options.baseRequestId
|
|
1043
|
+
} : {}),
|
|
1044
|
+
...(method === 'meta' && onlyViews ? {
|
|
1045
|
+
onlyViews: true
|
|
882
1046
|
} : {})
|
|
883
1047
|
};
|
|
884
1048
|
const args = method === 'meta' ? [coreOptions] : [query, coreOptions];
|
|
1049
|
+
// `method` picks between overloaded `CubeApi` methods, so the call is
|
|
1050
|
+
// dispatched dynamically while keeping `cubeApi` as the receiver
|
|
1051
|
+
const fetchMethod = cubeApi[method];
|
|
885
1052
|
try {
|
|
886
|
-
const
|
|
1053
|
+
const fetchResponse = await fetchMethod.apply(cubeApi, args);
|
|
887
1054
|
setResponse({
|
|
888
|
-
response,
|
|
1055
|
+
response: fetchResponse,
|
|
889
1056
|
isLoading: false
|
|
890
1057
|
});
|
|
891
|
-
} catch (
|
|
892
|
-
setError(
|
|
1058
|
+
} catch (fetchError) {
|
|
1059
|
+
setError(fetchError);
|
|
893
1060
|
setResponse({
|
|
894
1061
|
isLoading: false,
|
|
895
1062
|
response: null
|
|
@@ -899,14 +1066,23 @@ function useCubeFetch(method, options = {}) {
|
|
|
899
1066
|
}
|
|
900
1067
|
useEffect(() => {
|
|
901
1068
|
load();
|
|
1069
|
+
// `order` is read off a single query; an array of queries does not carry it
|
|
902
1070
|
}, useDeepCompareMemoize([Object.keys(options.query?.order || {}), options, context]));
|
|
903
1071
|
return {
|
|
904
1072
|
...response,
|
|
905
1073
|
error,
|
|
906
|
-
refetch:
|
|
1074
|
+
refetch: refetchOptions => load(refetchOptions, true)
|
|
907
1075
|
};
|
|
908
1076
|
}
|
|
909
1077
|
|
|
1078
|
+
/**
|
|
1079
|
+
* The hook resolves with a `SqlQuery`, while its `response` stays declared as a
|
|
1080
|
+
* dry-run response for backwards compatibility — `UseCubeSqlResult` spells the
|
|
1081
|
+
* real shape. `refetch` is declared, as it is on `useDryRun` and `useCubeMeta`,
|
|
1082
|
+
* because all three return the same object.
|
|
1083
|
+
*
|
|
1084
|
+
* @hidden
|
|
1085
|
+
*/
|
|
910
1086
|
function useCubeSql(query, options = {}) {
|
|
911
1087
|
return useCubeFetch('sql', {
|
|
912
1088
|
...options,
|
|
@@ -914,6 +1090,9 @@ function useCubeSql(query, options = {}) {
|
|
|
914
1090
|
});
|
|
915
1091
|
}
|
|
916
1092
|
|
|
1093
|
+
/**
|
|
1094
|
+
* @hidden
|
|
1095
|
+
*/
|
|
917
1096
|
function useDryRun(query, options = {}) {
|
|
918
1097
|
return useCubeFetch('dryRun', {
|
|
919
1098
|
...options,
|
|
@@ -921,6 +1100,9 @@ function useDryRun(query, options = {}) {
|
|
|
921
1100
|
});
|
|
922
1101
|
}
|
|
923
1102
|
|
|
1103
|
+
/**
|
|
1104
|
+
* @hidden
|
|
1105
|
+
*/
|
|
924
1106
|
function useLazyDryRun(query, options = {}) {
|
|
925
1107
|
const {
|
|
926
1108
|
refetch,
|
|
@@ -933,6 +1115,42 @@ function useLazyDryRun(query, options = {}) {
|
|
|
933
1115
|
return [refetch, result];
|
|
934
1116
|
}
|
|
935
1117
|
|
|
1118
|
+
/**
|
|
1119
|
+
* A React hook for executing Cube.js queries
|
|
1120
|
+
* ```js
|
|
1121
|
+
* import React from 'react';
|
|
1122
|
+
* import { Table } from 'antd';
|
|
1123
|
+
* import { useCubeQuery } from '@cubejs-client/react';
|
|
1124
|
+
*
|
|
1125
|
+
* export default function App() {
|
|
1126
|
+
* const { resultSet, isLoading, error, progress } = useCubeQuery({
|
|
1127
|
+
* measures: ['Orders.count'],
|
|
1128
|
+
* dimensions: ['Orders.createdAt.month'],
|
|
1129
|
+
* });
|
|
1130
|
+
*
|
|
1131
|
+
* if (isLoading) {
|
|
1132
|
+
* return <div>{progress?.stage || 'Loading...'}</div>;
|
|
1133
|
+
* }
|
|
1134
|
+
*
|
|
1135
|
+
* if (error) {
|
|
1136
|
+
* return <div>{error.toString()}</div>;
|
|
1137
|
+
* }
|
|
1138
|
+
*
|
|
1139
|
+
* if (!resultSet) {
|
|
1140
|
+
* return null;
|
|
1141
|
+
* }
|
|
1142
|
+
*
|
|
1143
|
+
* const dataSource = resultSet.tablePivot();
|
|
1144
|
+
* const columns = resultSet.tableColumns();
|
|
1145
|
+
*
|
|
1146
|
+
* return <Table columns={columns} dataSource={dataSource} />;
|
|
1147
|
+
* }
|
|
1148
|
+
*
|
|
1149
|
+
* ```
|
|
1150
|
+
* @order 1
|
|
1151
|
+
* @stickyTypes
|
|
1152
|
+
*/
|
|
1153
|
+
|
|
936
1154
|
function useCubeQuery(query, options = {}) {
|
|
937
1155
|
const mutexRef = useRef({});
|
|
938
1156
|
const [currentQuery, setCurrentQuery] = useState(null);
|
|
@@ -942,9 +1160,9 @@ function useCubeQuery(query, options = {}) {
|
|
|
942
1160
|
const [error, setError] = useState(null);
|
|
943
1161
|
const context = useContext(CubeContext);
|
|
944
1162
|
let subscribeRequest = null;
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1163
|
+
|
|
1164
|
+
// `progressResponse` is not part of the public `ProgressResult` API
|
|
1165
|
+
const progressCallback = progressResult => setProgress(progressResult.progressResponse);
|
|
948
1166
|
async function fetch() {
|
|
949
1167
|
const {
|
|
950
1168
|
resetResultSetOnChange
|
|
@@ -970,8 +1188,8 @@ function useCubeQuery(query, options = {}) {
|
|
|
970
1188
|
});
|
|
971
1189
|
setResultSet(response);
|
|
972
1190
|
setProgress(null);
|
|
973
|
-
} catch (
|
|
974
|
-
setError(
|
|
1191
|
+
} catch (loadError) {
|
|
1192
|
+
setError(loadError);
|
|
975
1193
|
setResultSet(null);
|
|
976
1194
|
setProgress(null);
|
|
977
1195
|
}
|
|
@@ -988,7 +1206,10 @@ function useCubeQuery(query, options = {}) {
|
|
|
988
1206
|
}
|
|
989
1207
|
async function loadQuery() {
|
|
990
1208
|
if (!skip && isQueryPresent(query)) {
|
|
991
|
-
|
|
1209
|
+
// `areQueriesEqual` is declared for a single query, and reads no more
|
|
1210
|
+
// than `order` off one when given an array of queries
|
|
1211
|
+
const previousQuery = currentQuery;
|
|
1212
|
+
if (!areQueriesEqual(previousQuery, query)) {
|
|
992
1213
|
if (resetResultSetOnChange == null || resetResultSetOnChange) {
|
|
993
1214
|
setResultSet(null);
|
|
994
1215
|
}
|
|
@@ -1036,7 +1257,8 @@ function useCubeQuery(query, options = {}) {
|
|
|
1036
1257
|
subscribeRequest = null;
|
|
1037
1258
|
}
|
|
1038
1259
|
};
|
|
1039
|
-
|
|
1260
|
+
// `order` is read off a single query; an array of queries does not carry it
|
|
1261
|
+
}, useDeepCompareMemoize([query, Object.keys(query?.order || {}), options, context]));
|
|
1040
1262
|
return {
|
|
1041
1263
|
isLoading,
|
|
1042
1264
|
resultSet,
|