Skip to content

Commit

Permalink
Renaming, refactoring, and using useSyncExternalStore hook from React
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenthoms committed Oct 3, 2023
1 parent 37903b1 commit 2073516
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 44 deletions.
68 changes: 36 additions & 32 deletions frontend/src/framework/GuiMessageBroker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,40 +84,36 @@ export class GuiMessageBroker {
}
}

addEventListener<K extends GuiEvent>(event: K, listener: (event: GuiEventPayloads[K]) => void) {
subscribeToEvent<K extends GuiEvent>(event: K, callback: (event: GuiEventPayloads[K]) => void) {
const eventListeners = this._eventListeners.get(event) || new Set();
eventListeners.add(listener);
eventListeners.add(callback);
this._eventListeners.set(event, eventListeners);
}

removeEventListener<K extends GuiEvent>(event: K, listener: (event: GuiEventPayloads[K]) => void) {
const eventListeners = this._eventListeners.get(event);
if (eventListeners) {
eventListeners.delete(listener);
}
return () => {
eventListeners.delete(callback);
};
}

dispatchEvent<K extends GuiEvent>(event: K, details: GuiEventPayloads[K]) {
console.debug("dispatching event", event, details);
publishEvent<K extends GuiEvent>(event: K, details: GuiEventPayloads[K]) {
const eventListeners = this._eventListeners.get(event);
if (eventListeners) {
eventListeners.forEach((listener) => listener({ ...details }));
eventListeners.forEach((callback) => callback({ ...details }));
}
}

addStateSubscriber<K extends GuiState>(state: K, subscriber: (state: GuiStateTypes[K]) => void): () => void {
const stateSubscribers = this._stateSubscribers.get(state) || new Set();
stateSubscribers.add(subscriber);

this._stateSubscribers.set(state, stateSubscribers);
makeStateSubscriberFunction<K extends GuiState>(state: K): (onStoreChangeCallback: () => void) => () => void {
// Using arrow function in order to keep "this" in context
const stateSubscriber = (onStoreChangeCallback: () => void): (() => void) => {
const stateSubscribers = this._stateSubscribers.get(state) || new Set();
stateSubscribers.add(onStoreChangeCallback);
this._stateSubscribers.set(state, stateSubscribers);

if (this._storedValues.has(state)) {
subscriber(this._storedValues.get(state));
}

return () => {
stateSubscribers.delete(subscriber);
return () => {
stateSubscribers.delete(onStoreChangeCallback);
};
};

return stateSubscriber;
}

setState<K extends GuiState>(state: K, value: GuiStateTypes[K]) {
Expand All @@ -133,22 +129,30 @@ export class GuiMessageBroker {
getState<K extends GuiState>(state: K): GuiStateTypes[K] {
return this._storedValues.get(state);
}

/*
It is really important that the snapshot returned by "stateSnapshotGetter"
returns the same value as long as the state has not been changed.
*/
makeStateSnapshotGetter<K extends GuiState>(state: K): () => GuiStateTypes[K] {
// Using arrow function in order to keep "this" in context
const stateSnapshotGetter = (): GuiStateTypes[K] => {
return this._storedValues.get(state);
};

return stateSnapshotGetter;
}
}

export function useGuiState<T extends GuiState>(
guiMessageBroker: GuiMessageBroker,
key: T
): [GuiStateTypes[T], (value: GuiStateTypes[T] | ((prev: GuiStateTypes[T]) => GuiStateTypes[T])) => void] {
const [state, setState] = React.useState<GuiStateTypes[T]>(guiMessageBroker.getState(key));

React.useEffect(() => {
const handleStateChange = (value: GuiStateTypes[T]) => {
setState(value);
};

const unsubscribeFunc = guiMessageBroker.addStateSubscriber(key, handleStateChange);
return unsubscribeFunc;
}, [key, guiMessageBroker]);
const state = React.useSyncExternalStore<GuiStateTypes[T]>(
guiMessageBroker.makeStateSubscriberFunction(key),
guiMessageBroker.makeStateSnapshotGetter(key)
);

function setter(valueOrFunc: GuiStateTypes[T] | ((prev: GuiStateTypes[T]) => GuiStateTypes[T])): void {
if (valueOrFunc instanceof Function) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const ViewWrapper: React.FC<ViewWrapperProps> = (props) => {
if (ref.current) {
const point = pointerEventToPoint(e.nativeEvent);
const rect = ref.current.getBoundingClientRect();
guiMessageBroker.dispatchEvent(GuiEvent.ModuleHeaderPointerDown, {
guiMessageBroker.publishEvent(GuiEvent.ModuleHeaderPointerDown, {
moduleInstanceId: props.moduleInstance.getId(),
elementPosition: pointDifference(point, pointRelativeToDomRect(point, rect)),
pointerPosition: point,
Expand All @@ -59,7 +59,7 @@ export const ViewWrapper: React.FC<ViewWrapperProps> = (props) => {

const handleRemoveClick = React.useCallback(
function handleRemoveClick(e: React.PointerEvent<HTMLDivElement>) {
guiMessageBroker.dispatchEvent(GuiEvent.RemoveModuleInstanceRequest, {
guiMessageBroker.publishEvent(GuiEvent.RemoveModuleInstanceRequest, {
moduleInstanceId: props.moduleInstance.getId(),
});
e.preventDefault();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,21 +237,28 @@ export const Layout: React.FC<LayoutProps> = (props) => {
props.workbench.setLayout(currentLayout);
};

guiMessageBroker.addEventListener(GuiEvent.ModuleHeaderPointerDown, handleModuleHeaderPointerDown);
guiMessageBroker.addEventListener(GuiEvent.NewModulePointerDown, handleNewModulePointerDown);
guiMessageBroker.addEventListener(GuiEvent.RemoveModuleInstanceRequest, handleRemoveModuleInstanceRequest);
const removeModuleHeaderPointerDownSubscriber = guiMessageBroker.subscribeToEvent(
GuiEvent.ModuleHeaderPointerDown,
handleModuleHeaderPointerDown
);
const removeNewModulePointerDownSubscriber = guiMessageBroker.subscribeToEvent(
GuiEvent.NewModulePointerDown,
handleNewModulePointerDown
);
const removeRemoveModuleInstanceRequestSubscriber = guiMessageBroker.subscribeToEvent(
GuiEvent.RemoveModuleInstanceRequest,
handleRemoveModuleInstanceRequest
);

document.addEventListener("pointerup", handlePointerUp);
document.addEventListener("pointermove", handlePointerMove);
document.addEventListener("keydown", handleButtonClick);

return () => {
guiMessageBroker.removeEventListener(GuiEvent.ModuleHeaderPointerDown, handleModuleHeaderPointerDown);
guiMessageBroker.removeEventListener(GuiEvent.NewModulePointerDown, handleNewModulePointerDown);
guiMessageBroker.removeEventListener(
GuiEvent.RemoveModuleInstanceRequest,
handleRemoveModuleInstanceRequest
);
removeModuleHeaderPointerDownSubscriber();
removeNewModulePointerDownSubscriber();
removeRemoveModuleInstanceRequestSubscriber();

document.removeEventListener("pointerup", handlePointerUp);
document.removeEventListener("pointermove", handlePointerMove);
document.removeEventListener("keydown", handleButtonClick);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const ModulesListItem: React.FC<ModulesListItemProps> = (props) => {
const point = pointerEventToPoint(e);
const rect = ref.current.getBoundingClientRect();
pointerDownElementPosition = pointDifference(point, pointRelativeToDomRect(point, rect));
props.guiMessageBroker.dispatchEvent(GuiEvent.NewModulePointerDown, {
props.guiMessageBroker.publishEvent(GuiEvent.NewModulePointerDown, {
moduleName: props.name,
elementPosition: pointDifference(point, pointRelativeToDomRect(point, rect)),
pointerPosition: point,
Expand Down

0 comments on commit 2073516

Please sign in to comment.