2022-09-14 14:54:09 -05:00
|
|
|
import { useEffect } from 'react';
|
2022-11-03 02:29:39 -05:00
|
|
|
import { Observer, Subject, Subscription } from 'rxjs';
|
2022-09-14 14:54:09 -05:00
|
|
|
|
|
|
|
import { useForceUpdate } from '@grafana/ui';
|
|
|
|
|
|
|
|
export class StateManagerBase<TState> {
|
2022-11-03 02:29:39 -05:00
|
|
|
private _subject = new Subject<TState>();
|
|
|
|
private _state: TState;
|
2022-09-14 14:54:09 -05:00
|
|
|
|
|
|
|
constructor(state: TState) {
|
2022-11-03 02:29:39 -05:00
|
|
|
this._state = state;
|
2022-09-14 14:54:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
useState() {
|
|
|
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
|
|
return useLatestState(this);
|
|
|
|
}
|
|
|
|
|
2022-11-03 02:29:39 -05:00
|
|
|
get state() {
|
|
|
|
return this._state;
|
|
|
|
}
|
|
|
|
|
2022-09-14 14:54:09 -05:00
|
|
|
setState(update: Partial<TState>) {
|
2022-11-03 02:29:39 -05:00
|
|
|
this._state = {
|
|
|
|
...this._state,
|
2022-09-14 14:54:09 -05:00
|
|
|
...update,
|
|
|
|
};
|
2022-11-03 02:29:39 -05:00
|
|
|
this._subject.next(this._state);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Subscribe to the scene state subject
|
|
|
|
**/
|
|
|
|
subscribeToState(observerOrNext?: Partial<Observer<TState>>): Subscription {
|
|
|
|
return this._subject.subscribe(observerOrNext);
|
2022-09-14 14:54:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* This hook is always returning model.state instead of a useState that remembers the last state emitted on the subject
|
|
|
|
* The reason for this is so that if the model instance change this function will always return the latest state.
|
|
|
|
*/
|
|
|
|
function useLatestState<TState>(model: StateManagerBase<TState>): TState {
|
|
|
|
const forceUpdate = useForceUpdate();
|
|
|
|
|
|
|
|
useEffect(() => {
|
2022-11-03 02:29:39 -05:00
|
|
|
const s = model.subscribeToState({ next: forceUpdate });
|
2022-09-14 14:54:09 -05:00
|
|
|
return () => s.unsubscribe();
|
|
|
|
}, [model, forceUpdate]);
|
|
|
|
|
|
|
|
return model.state;
|
|
|
|
}
|