import { UI_PRIORITY } from 'client-run-queue'; import React, { useRef } from 'react'; import type { BindingDependencies, NamedBindingDependencies } from '../../binding/types/binding-dependencies'; import { isBinding } from '../../binding-utils/type-utils.js'; import { extractBindingDependencyValues } from '../../internal-utils/extract-binding-dependency-values.js'; import { getTypedKeys } from '../../internal-utils/get-typed-keys.js'; import { pickLimiterOptions } from '../../limiter/pick-limiter-options.js'; import { useBindingEffect } from '../../use-binding-effect/use-binding-effect.js'; import { Refreshable } from './internal/Refreshable.js'; import type { BindingsConsumerProps } from './types/props'; import type { BindingsConsumerRenderCallback } from './types/render-callback'; export * from './types/exports.js'; /** * A component that is rerendered based on input binding changes. * * A BindingsConsumer must have exactly one function child. * * The general usage pattern is something like: * * ``` * * {({someBinding, anotherBinding}) => {someBinding} - {anotherBinding}} * * ``` */ export const BindingsConsumer = ( props: BindingsConsumerProps & { children: BindingsConsumerRenderCallback; } ) => { const { children, // BindingsConsumerProps bindings, areInputValuesEqual, detectInputChanges = false, makeComparableInputValue } = props; const limiterOptions = pickLimiterOptions(props); if (limiterOptions.priority === undefined) { limiterOptions.priority = UI_PRIORITY; } const isNonNamedBindings = Array.isArray(bindings) || isBinding(bindings); const namedBindings = isNonNamedBindings ? undefined : (bindings as NamedBindingDependencies); const namedBindingsKeys = namedBindings !== undefined ? getTypedKeys(namedBindings) : undefined; // Doesn't need to be stable since Refreshable will always get rendered with the latest anyway const getDependencies = () => bindings as DependenciesT; // Doesn't need to be stable since Refreshable will always get rendered with the latest anyway // eslint-disable-next-line @typescript-eslint/no-unsafe-return const getDependencyValues = () => extractBindingDependencyValues({ bindings, namedBindingsKeys }); const refreshControls = useRef<{ refresh?: () => void }>({}); const cancelLastPendingRefresh = useBindingEffect(bindings, () => refreshControls.current.refresh?.(), { areInputValuesEqual, detectInputChanges, makeComparableInputValue, ...limiterOptions }); return ( ); };