Skip to content

Commit c4e287e

Browse files
authored
Merge pull request #127 from Kitware/suspense-hook
Add onViewReadySuspense hook
2 parents 5bd0ec1 + d47f388 commit c4e287e

23 files changed

+226
-40
lines changed

src/core/Algorithm.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { usePrevious } from '../utils/usePrevious';
88
import useUnmount from '../utils/useUnmount';
99
import {
1010
DownstreamContext,
11-
useDownstream,
12-
useRepresentation,
11+
useDownstreamContext,
12+
useRepresentationContext,
1313
} from './contexts';
1414

1515
export interface AlgorithmProps extends PropsWithChildren {
@@ -46,8 +46,8 @@ export default function Algorithm(props: AlgorithmProps) {
4646
return algo;
4747
});
4848

49-
const representation = useRepresentation();
50-
const downstream = useDownstream();
49+
const representation = useRepresentationContext();
50+
const downstream = useDownstreamContext();
5151

5252
useEffect(() => {
5353
let algoChanged = false;

src/core/CellData.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { PropsWithChildren, useCallback } from 'react';
2-
import { FieldDataContext, useDataset } from './contexts';
2+
import { FieldDataContext, useDatasetContext } from './contexts';
33

44
export default function CellData(props: PropsWithChildren) {
5-
const dataset = useDataset();
5+
const dataset = useDatasetContext();
66
const getCellData = useCallback(() => {
77
return dataset.getDataSet().getCellData();
88
}, [dataset]);

src/core/DataArray.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import deletionRegistry from '../utils/DeletionRegistry';
88
import useGetterRef from '../utils/useGetterRef';
99
import { usePrevious } from '../utils/usePrevious';
1010
import useUnmount from '../utils/useUnmount';
11-
import { useDataset, useFieldData } from './contexts';
11+
import { useDatasetContext, useFieldDataContext } from './contexts';
1212

1313
export interface DataArrayProps {
1414
/**
@@ -61,8 +61,8 @@ export default function DataArray(props: DataArrayProps) {
6161
return da;
6262
});
6363

64-
const getFieldData = useFieldData();
65-
const dataset = useDataset();
64+
const getFieldData = useFieldDataContext();
65+
const dataset = useDatasetContext();
6666

6767
const { registration = DefaultProps.registration } = props;
6868

src/core/Dataset.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { vtkObject } from '@kitware/vtk.js/interfaces';
22
import { useEffect } from 'react';
3-
import { useDownstream, useRepresentation } from './contexts';
3+
import { useDownstreamContext, useRepresentationContext } from './contexts';
44

55
export interface DatasetProps {
66
dataset: vtkObject | null;
77
}
88

99
export default function Dataset(props: DatasetProps) {
10-
const representation = useRepresentation();
11-
const downstream = useDownstream();
10+
const representation = useRepresentationContext();
11+
const downstream = useDownstreamContext();
1212

1313
const { dataset } = props;
1414

src/core/FieldData.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { PropsWithChildren, useCallback } from 'react';
2-
import { FieldDataContext, useDataset } from './contexts';
2+
import { FieldDataContext, useDatasetContext } from './contexts';
33

44
export default function FieldData(props: PropsWithChildren) {
5-
const dataset = useDataset();
5+
const dataset = useDatasetContext();
66
const getFieldData = useCallback(() => {
77
return dataset.getDataSet().getFieldData();
88
}, [dataset]);

src/core/ImageData.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import { IDataset } from '../types';
1111
import deletionRegistry from '../utils/DeletionRegistry';
1212
import useGetterRef from '../utils/useGetterRef';
1313
import useUnmount from '../utils/useUnmount';
14-
import { DatasetContext, useDownstream, useRepresentation } from './contexts';
14+
import {
15+
DatasetContext,
16+
useDownstreamContext,
17+
useRepresentationContext,
18+
} from './contexts';
1519

1620
export interface ImageDataProps extends PropsWithChildren {
1721
/**
@@ -63,8 +67,8 @@ export default forwardRef(function PolyData(props: ImageDataProps, fwdRef) {
6367
return im;
6468
});
6569

66-
const representation = useRepresentation();
67-
const downstream = useDownstream();
70+
const representation = useRepresentationContext();
71+
const downstream = useDownstreamContext();
6872

6973
// dataset API
7074
const dataset = useMemo<IDataset<vtkImageData>>(

src/core/PointData.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { PropsWithChildren, useCallback } from 'react';
2-
import { FieldDataContext, useDataset } from './contexts';
2+
import { FieldDataContext, useDatasetContext } from './contexts';
33

44
export default function PointData(props: PropsWithChildren) {
5-
const dataset = useDataset();
5+
const dataset = useDatasetContext();
66
const getPointData = useCallback(() => {
77
return dataset.getDataSet().getPointData();
88
}, [dataset]);

src/core/PolyData.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ import deletionRegistry from '../utils/DeletionRegistry';
1717
import useGetterRef from '../utils/useGetterRef';
1818
import { usePrevious } from '../utils/usePrevious';
1919
import useUnmount from '../utils/useUnmount';
20-
import { DatasetContext, useDownstream, useRepresentation } from './contexts';
20+
import {
21+
DatasetContext,
22+
useDownstreamContext,
23+
useRepresentationContext,
24+
} from './contexts';
2125

2226
export interface PolyDataProps extends PropsWithChildren {
2327
/**
@@ -103,8 +107,8 @@ export default forwardRef(function PolyData(props: PolyDataProps, fwdRef) {
103107
return pd;
104108
});
105109

106-
const representation = useRepresentation();
107-
const downstream = useDownstream();
110+
const representation = useRepresentationContext();
111+
const downstream = useDownstreamContext();
108112

109113
// dataset API
110114
const dataset = useMemo<IDataset<vtkPolyData>>(

src/core/Reader.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import vtk from '@kitware/vtk.js/vtk';
44
import { PropsWithChildren, useCallback, useEffect, useRef } from 'react';
55
import { VtkConstructor } from '../types';
66
import deletionRegistry from '../utils/DeletionRegistry';
7-
import { useDownstream, useRepresentation } from './contexts';
7+
import { useDownstreamContext, useRepresentationContext } from './contexts';
88

99
export interface ReaderProps extends PropsWithChildren {
1010
/**
@@ -71,7 +71,7 @@ export default function Reader(props: ReaderProps) {
7171
);
7272
}
7373

74-
const representation = useRepresentation();
74+
const representation = useRepresentationContext();
7575

7676
const createReader = useCallback(() => {
7777
if (typeof vtkClass === 'string') {
@@ -138,7 +138,7 @@ export default function Reader(props: ReaderProps) {
138138

139139
// --- downstream registration --- //
140140

141-
const downstream = useDownstream();
141+
const downstream = useDownstreamContext();
142142
const { port } = props;
143143

144144
useEffect(() => {

src/core/ShareDataSet.tsx

+7-7
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import {
2727
DownstreamContext,
2828
RepresentationContext,
2929
ShareDataSetContext,
30-
useDownstream,
31-
useRepresentation,
32-
useShareDataSet,
30+
useDownstreamContext,
31+
useRepresentationContext,
32+
useShareDataSetContext,
3333
} from './contexts';
3434
import useDataEvents from './modules/useDataEvents';
3535

@@ -138,7 +138,7 @@ export interface RegisterDataSetProps extends PropsWithChildren {
138138
}
139139

140140
export function RegisterDataSet(props: RegisterDataSetProps) {
141-
const share = useShareDataSet();
141+
const share = useShareDataSetContext();
142142
const { id } = props;
143143

144144
// --- handle registrations --- //
@@ -206,10 +206,10 @@ export interface UseDataSetProps extends PropsWithChildren {
206206

207207
export function UseDataSet(props: UseDataSetProps) {
208208
const { id, port = 0 } = props;
209-
const share = useShareDataSet();
209+
const share = useShareDataSetContext();
210210
// TODO if useDataSet is input to an algorithm, should representation be null?
211-
const representation = useRepresentation();
212-
const downstream = useDownstream();
211+
const representation = useRepresentationContext();
212+
const downstream = useDownstreamContext();
213213

214214
useEffect(() => {
215215
return share.onDataAvailable(id, (ds) => {

src/core/View.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default forwardRef(function View(props: ViewProps, fwdRef) {
2424
multiViewRoot ? parentedViewRef.current : singleViewRef.current;
2525
return {
2626
isInMultiViewRoot: () => multiViewRoot,
27+
isMounted: () => getView()?.isMounted() ?? false,
2728
getViewContainer: () => getView()?.getViewContainer() ?? null,
2829
getOpenGLRenderWindow: () => getView()?.getOpenGLRenderWindow() ?? null,
2930
getRenderWindow: () => getView()?.getRenderWindow() ?? null,

src/core/VolumeController.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Contexts } from '..';
55
import deletionRegistry from '../utils/DeletionRegistry';
66
import useGetterRef from '../utils/useGetterRef';
77
import useUnmount from '../utils/useUnmount';
8-
import { useRepresentation } from './contexts';
8+
import { useRepresentationContext } from './contexts';
99

1010
export interface VolumeControllerProps {
1111
/**
@@ -35,7 +35,7 @@ export default function VolumeController(props: VolumeControllerProps) {
3535
const view = useContext(Contexts.ViewContext);
3636
if (!view) throw new Error('Need view context');
3737

38-
const volumeRep = useRepresentation();
38+
const volumeRep = useRepresentationContext();
3939

4040
const updateWidget = useCallback(() => {
4141
const volume = volumeRep.getMapper()?.getInputData() as

src/core/contexts.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ export const MultiViewRootContext = createContext<boolean>(false);
3737

3838
export const ViewContext = createContext<IView | null>(null);
3939

40+
export function useViewContext() {
41+
const view = useContext(ViewContext);
42+
if (!view) throw new Error('No View context!');
43+
return view;
44+
}
45+
4046
export function useRenderWindowContext() {
4147
const rw = useContext(RenderWindowContext);
4248
if (!rw) throw new Error('No RenderWindow context!');
@@ -49,31 +55,31 @@ export function useRendererContext() {
4955
return r;
5056
}
5157

52-
export function useFieldData<T = vtkFieldData>() {
58+
export function useFieldDataContext<T = vtkFieldData>() {
5359
const fd = useContext(FieldDataContext);
5460
if (!fd) throw new Error('No FieldData context!');
5561
return fd as () => T;
5662
}
5763

58-
export function useDataset<T = vtkDataSet>() {
64+
export function useDatasetContext<T = vtkDataSet>() {
5965
const ds = useContext(DatasetContext);
6066
if (!ds) throw new Error('No Dataset context!');
6167
return ds as IDataset<T>;
6268
}
6369

64-
export function useRepresentation() {
70+
export function useRepresentationContext() {
6571
const rep = useContext(RepresentationContext);
6672
if (!rep) throw new Error('No Representation context!');
6773
return rep;
6874
}
6975

70-
export function useDownstream() {
76+
export function useDownstreamContext() {
7177
const ds = useContext(DownstreamContext);
7278
if (!ds) throw new Error('No Downstream context!');
7379
return ds;
7480
}
7581

76-
export function useShareDataSet() {
82+
export function useShareDataSetContext() {
7783
const share = useContext(ShareDataSetContext);
7884
if (!share) throw new Error('No ShareDataSet context!');
7985
return share;

src/core/internal/ParentedView.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
} from '../modules/useInteractorStyle';
2828
import useViewEvents, { ViewEvents } from '../modules/useViewEvents';
2929
import Renderer from '../Renderer';
30+
import { viewMountedEvent } from './events';
3031
import { DefaultProps, ViewProps } from './view-shared';
3132

3233
/**
@@ -172,9 +173,16 @@ const ParentedView = forwardRef(function ParentedView(
172173

173174
// --- api --- //
174175

176+
let mounted = false;
177+
useMount(() => {
178+
mounted = true;
179+
viewMountedEvent.trigger();
180+
});
181+
175182
const api = useMemo<IView>(
176183
() => ({
177184
isInMultiViewRoot: () => true,
185+
isMounted: () => mounted,
178186
getViewContainer: () => containerRef.current,
179187
getOpenGLRenderWindow: () => openGLRenderWindowAPI,
180188
getRenderWindow: () => renderWindowAPI,
@@ -187,6 +195,7 @@ const ParentedView = forwardRef(function ParentedView(
187195
rendererRef.current?.resetCamera(boundsToUse),
188196
}),
189197
[
198+
mounted,
190199
openGLRenderWindowAPI,
191200
renderWindowAPI,
192201
getInteractorStyle,

src/core/internal/SingleView.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import useViewEvents, { ViewEvents } from '../modules/useViewEvents';
2424
import OpenGLRenderWindow from '../OpenGLRenderWindow';
2525
import Renderer from '../Renderer';
2626
import RenderWindow from '../RenderWindow';
27+
import { viewMountedEvent } from './events';
2728
import { DefaultProps, ViewProps } from './view-shared';
2829

2930
/**
@@ -84,9 +85,16 @@ const SingleView = forwardRef(function SingleView(props: ViewProps, fwdRef) {
8485

8586
// --- api --- //
8687

88+
let mounted = false;
89+
useMount(() => {
90+
mounted = true;
91+
viewMountedEvent.trigger();
92+
});
93+
8794
const api = useMemo<IView>(
8895
() => ({
8996
isInMultiViewRoot: () => false,
97+
isMounted: () => mounted,
9098
getViewContainer: () =>
9199
openGLRenderWindowRef.current?.getContainer() ?? null,
92100
getOpenGLRenderWindow: () => openGLRenderWindowRef.current,
@@ -99,7 +107,7 @@ const SingleView = forwardRef(function SingleView(props: ViewProps, fwdRef) {
99107
resetCamera: (boundsToUse?: Bounds) =>
100108
rendererRef.current?.resetCamera(boundsToUse),
101109
}),
102-
[getInteractorStyle, setInteractorStyle]
110+
[mounted, getInteractorStyle, setInteractorStyle]
103111
);
104112

105113
useImperativeHandle(fwdRef, () => api);

src/core/internal/events.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
export type EventListener = (...args: any[]) => void;
2+
3+
function createEvent() {
4+
const callbacks: EventListener[] = [];
5+
6+
const off = (callback: EventListener) => {
7+
const idx = callbacks.indexOf(callback);
8+
if (idx === -1) return;
9+
callbacks.splice(idx, 1);
10+
};
11+
12+
const on = (callback: EventListener) => {
13+
callbacks.push(callback);
14+
return () => off(callback);
15+
};
16+
17+
const once = (callback: EventListener) => {
18+
const stop = on(() => {
19+
stop();
20+
callback();
21+
});
22+
};
23+
24+
const trigger = (...args: any[]) => {
25+
callbacks.forEach((cb) => {
26+
cb(...args);
27+
});
28+
};
29+
30+
return { off, on, once, trigger };
31+
}
32+
33+
export const viewMountedEvent = createEvent();

src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import '@kitware/vtk.js/Rendering/OpenGL/Profiles/Volume';
66
export { default as Algorithm } from './core/Algorithm';
77
export type { AlgorithmProps } from './core/Algorithm';
88
export { default as CellData } from './core/CellData';
9+
export * from './core/contexts';
910
export * as Contexts from './core/contexts';
1011
export { default as DataArray } from './core/DataArray';
1112
export type { DataArrayProps } from './core/DataArray';
@@ -46,3 +47,4 @@ export { default as View } from './core/View';
4647
export type { ViewProps } from './core/View';
4748
export { default as VolumeController } from './core/VolumeController';
4849
export { default as VolumeRepresentation } from './core/VolumeRepresentation';
50+
export * from './suspense';

src/suspense/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useViewReadySuspense';

0 commit comments

Comments
 (0)