newContext

The newContext function creates a context handler for managing key-value pairs in a stack of context objects, allowing retrieval and storage of values.

The getContextValue function provides a mechanism to retrieve or initialize a value associated with a specific key within a stack of context objects. It uses interally the newContext function.

Implementation
export function newContext<T>(
  key: string,
  required: boolean = false,
): (
  ...stack: Record<string, unknown>[]
) => [() => T | undefined, (value: T) => T] {
  return (...stack: Record<string, unknown>[]) => [
    function get() {
      for (const s of stack) {
        if (key in s) {
          return s[key] as T;
        }
      }
      if (required) {
        throw new Error(`Value for the key "${key}" was not found.`);
      }
      return undefined;
    },
    function set(value: T) {
      const [store] = stack;
      if (!store) {
        throw new Error("Stack is empty - no store");
      }
      store[key] = value;
      return value;
    },
  ];
}

export function getContextValue<T>(
  key: string,
  create: () => T,
): (...stack: Record<string, unknown>[]) => T {
  const useContext = newContext<T>(key);
  return (...stack: Record<string, unknown>[]) => {
    const [get, set] = useContext(...stack);
    return get() ?? set(create());
  };
}

This method is particularly useful in scenarios where hierarchical context management is required, such as:

Examples

newContext method examples:

const stack = [{}, {}];
const useContext = newContext<number>("counter");
const [get, set] = useContext(...stack);

console.log(get()); // Outputs: undefined
set(42);
console.log(get()); // Outputs: 42
const stack = [{}, { level: 1 }];
const useLevelContext = newContext<number>("level");
const [getLevel, setLevel] = useLevelContext(...stack);

console.log(getLevel()); // Outputs: 1 (found in the second context object)

// Sets a new value in the first stack object:
setLevel(2);
console.log(getLevel()); // Outputs: 2
console.log(stack); // Outputs: [ { level: 2 }, { level: 1 } ]
delete stack[0].level; // Remove the 2
console.log(getLevel()); // Outputs: 1
console.log(stack); // Outputs: [ {}, { level: 1 } ]

getContextValue method examples:

const stack = [{}, {}];
const getValue = getContextValue("myKey", () => "defaultValue");

console.log(getValue(...stack)); // Outputs: "defaultValue"
stack[0]["myKey"] = "customValue";
console.log(getValue(...stack)); // Outputs: "customValue"
const stack = [{ parent: "root" }, { child: "leaf" }];
const getChildValue = getContextValue("child", () => "defaultChild");
const getParentValue = getContextValue("parent", () => "defaultParent");

console.log(getChildValue(...stack)); // Outputs: "leaf"
console.log(getParentValue(...stack)); // Outputs: "root"

stack[1]["parent"] = "overriddenRoot";
console.log(getParentValue(...stack)); // Outputs: "overriddenRoot"

newContext

The newContext function underpins the behavior of getContextValue by providing getter and setter functions for a specific key.

function newContext<T>(
  key: string,
  required: boolean = false
): (
  ...stack: Record<string, unknown>[]
) => [get: () => T | undefined, set: (value: T) => T];

Parameters:

Returns a function that:

getContextValue method

function getContextValue<T>(
  key: string,
  create: () => T,
): (...stack: Record<string, unknown>[]) => T;

Parameters

Returns a function that:

Behavior Details