Selective Context Dispatches and Listeners

13 views Asked by At

I've been working on an approach to selective context listening/dispatching, without using any 3rd party libraries. I think solution is working now but I wondered if any more experienced React developers had any thoughts? This is the main framework of the implementation. I have versions of it at the root of my app, for numbers/strings/string arrays. It's a handy way to bootstrap shared state when I'm building new UI sections, but still hinders performance in certain situations where there is a heavy update overhead (e.g. rending D3 graphs).

Manager:

import { MutableRefObject, useEffect, useReducer, useRef } from 'react';

export interface UpdateRefInterface<T> {
  [key: string]: SelectiveListeners<T>;
}

export interface SelectiveListeners<T> {
  [key: string]: (update: T) => void;
}

export interface LatestValueRef<T> {
  [key: string]: T;
}

export interface UpdateAction<T> {
  contextKey: string;
  value: T;
}

export function useSelectiveContextManager<T>(
  initialContext: LatestValueRef<T>
) {
  const triggerUpdateRef = useRef({} as UpdateRefInterface<T>);

  const latestValueRef = useRef(initialContext);

  const dispatch = (action: UpdateAction<T>) => {
    const { contextKey, value } = action;
    const currentElement = latestValueRef.current[contextKey];
    const listeners = triggerUpdateRef.current[contextKey];

    if (!listeners) {
      console.log('about to log an error:', listeners);
      throw new Error(
        `No listeners found for this context: ${contextKey} with value ${value}`
      );
    }

    if (currentElement !== value) {
      try {
        Object.values(listeners).forEach((l) => {
          l(value);
        });
      } catch (e) {
        console.error(e);
      }
      latestValueRef.current[contextKey] = value;
      // }
    }
  };

  return { dispatch, triggerUpdateRef, contextRef: latestValueRef };
}

Listener:

export function useSelectiveContextListener<T>(
  contextKey: string,
  listenerKey: string,
  fallBackValue: T,
  updateRefContext: React.Context<
    React.MutableRefObject<UpdateRefInterface<T>>
  >,
  latestValueRefContext: React.Context<
    React.MutableRefObject<LatestValueRef<T>>
  >
) {
  const updateTriggers = useContext(updateRefContext);
  const latestRef = useContext(latestValueRefContext);
  let currentValue: LatestValueRef<T> | undefined = undefined;
  try {
    currentValue = latestRef.current;
  } catch (e) {
    console.error(e);
    console.log(
      'With: ',
      contextKey,
      listenerKey,
      updateRefContext,
      latestValueRefContext,
      fallBackValue
    );
  }

  const initialValue =
    currentValue === undefined ||
    currentValue === null ||
    currentValue[contextKey] === undefined
      ? fallBackValue
      : latestRef.current[contextKey];
  const [currentState, setCurrentState] = useState<T>(initialValue);

  let currentListeners = updateTriggers.current[contextKey];
  if (currentListeners === undefined) {
    updateTriggers.current[contextKey] = {};
    currentListeners = updateTriggers.current[contextKey];
    currentListeners[listenerKey] = setCurrentState;
  }

  useEffect(() => {
    currentListeners[listenerKey] = setCurrentState;
    setCurrentState(() => latestRef.current[contextKey]);

    return () => {
      if (currentListeners) {
        delete currentListeners[listenerKey];
      }
    };
  }, [currentListeners, contextKey, listenerKey, latestRef]);

  return { currentState, latestRef, setCurrentState, updateTriggers };
}

Dispatcher:

export function useSelectiveContextDispatch<T>(
  contextKey: string,
  listenerKey: string,
  initialValue: T,
  UpdateTriggerRefContext: React.Context<
    MutableRefObject<UpdateRefInterface<T>>
  >,
  dispatchUpdateContext: React.Context<(value: UpdateAction<T>) => void>,
  latestValueRefContext: React.Context<MutableRefObject<LatestValueRef<T>>>
) {
  const { currentState, latestRef, updateTriggers } =
    useSelectiveContextListener(
      contextKey,
      listenerKey,
      initialValue,
      UpdateTriggerRefContext,
      latestValueRefContext
    );

  const dispatchUpdate = useContext(dispatchUpdateContext);
  const [isInitialized, setIsInitialized] = useState(false);

  useEffect(() => {
    if (latestRef.current[contextKey] === undefined) {
      latestRef.current[contextKey] = initialValue;
    }
  }, [latestRef, initialValue, contextKey]);

  useEffect(() => {
    if (!isInitialized) {
      dispatchUpdate({ contextKey: contextKey, value: initialValue });
      if (currentState === initialValue) {
        setIsInitialized(true);
      } else {
      }
    }
  }, [currentState, isInitialized, contextKey, initialValue, dispatchUpdate]);
  return { currentState, dispatchUpdate };
}
type here
0

There are 0 answers