grungnet/src/helpers/useNetworkedState.js

83 lines
2.4 KiB
JavaScript
Raw Normal View History

2021-01-01 02:06:56 +00:00
import { useEffect, useState, useRef, useCallback } from "react";
2021-01-01 02:06:56 +00:00
import useDebounce from "./useDebounce";
import { diff, applyChanges } from "./diff";
2021-01-01 02:06:56 +00:00
function useNetworkedState(
defaultState,
session,
eventName,
debounceRate = 100,
partialUpdates = true
2021-01-01 02:06:56 +00:00
) {
const [state, _setState] = useState(defaultState);
// Used to control whether the state needs to be sent to the socket
2021-01-01 02:06:56 +00:00
const dirtyRef = useRef(false);
// Used to force a full update
const forceUpdateRef = useRef(false);
// Update dirty at the same time as state
const setState = useCallback((update, sync = true, force = false) => {
2021-01-01 02:06:56 +00:00
dirtyRef.current = sync;
forceUpdateRef.current = force;
2021-01-01 02:25:19 +00:00
_setState(update);
2020-12-31 06:56:51 +00:00
}, []);
2021-01-01 02:06:56 +00:00
const eventNameRef = useRef(eventName);
useEffect(() => {
eventNameRef.current = eventName;
}, [eventName]);
const debouncedState = useDebounce(state, debounceRate);
const lastSyncedStateRef = useRef();
useEffect(() => {
2021-01-01 02:06:56 +00:00
if (session.socket && dirtyRef.current) {
// If partial updates enabled, send just the changes to the socket
if (
lastSyncedStateRef.current &&
debouncedState &&
partialUpdates &&
!forceUpdateRef.current
) {
const changes = diff(lastSyncedStateRef.current, debouncedState);
if (changes) {
session.socket.emit(`${eventName}_update`, changes);
}
} else {
session.socket.emit(eventName, debouncedState);
}
2021-01-01 02:06:56 +00:00
dirtyRef.current = false;
forceUpdateRef.current = false;
lastSyncedStateRef.current = debouncedState;
}
}, [session.socket, eventName, debouncedState, partialUpdates]);
useEffect(() => {
function handleSocketEvent(data) {
_setState(data);
lastSyncedStateRef.current = data;
}
function handleSocketUpdateEvent(changes) {
_setState((prevState) => {
let newState = { ...prevState };
applyChanges(newState, changes);
lastSyncedStateRef.current = newState;
return newState;
});
}
session.socket?.on(eventName, handleSocketEvent);
session.socket?.on(`${eventName}_update`, handleSocketUpdateEvent);
return () => {
session.socket?.off(eventName, handleSocketEvent);
session.socket?.off(`${eventName}_update`, handleSocketUpdateEvent);
};
2020-12-31 06:56:51 +00:00
}, [session.socket, eventName]);
return [state, setState];
}
export default useNetworkedState;