adobe / react-spectrum

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.
https://react-spectrum.adobe.com
Apache License 2.0
12.21k stars 1.07k forks source link

Export `useSlot` & `useRenderProps` #6552

Open ArrayKnight opened 1 month ago

ArrayKnight commented 1 month ago

Provide a general summary of the feature here

The useSlot & useRenderProps hooks are very useful utilities within the Provider suite. Since we're already able to utilize Provider and the many different component contexts (as well as our own), it would be nice to have full access to related utilities

๐Ÿค” Expected Behavior?

useSlot & useRenderProps are exported by react-aria-components

๐Ÿ˜ฏ Current Behavior

Not currently exported

๐Ÿ’ Possible Solution

No response

๐Ÿ”ฆ Context

I'm currently utilizing useSlot to determine if a slot has been implemented or not, which allows for a flag to be provided for logic / styling branching

I'm expecting to have to implement new custom components that will want to implement their own render props

๐Ÿ’ป Examples

No response

๐Ÿงข Your Company/Team

No response

๐Ÿ•ท Tracking Issue

No response

yihuiliao commented 4 weeks ago

At least for useRenderProps we're not too sure about exporting this and making it public. If you need to use it, we suggest that you just copy it for now since it's pretty straightforward.

ArrayKnight commented 2 weeks ago
export function callRenderProps<T extends object, R>(
  value: R | ((renderProps: T) => R),
  values: T
) {
  if (typeof value === 'function') {
    return (value as (renderProps: T) => R)(values);
  }

  return value;
}
import { type CSSProperties, type ReactNode, useMemo } from 'react';
import { type RenderProps } from '../../types';
import { callRenderProps } from '../../utils';

type RenderPropsHookOptions<T extends object> = RenderProps<T> & {
  values: T;
  defaultChildren?: ReactNode;
  defaultClassName?: string;
  defaultStyle?: CSSProperties;
};

export function useRenderProps<T extends object>({
  children: childrenProp,
  className: classNameProp,
  style: styleProp,
  defaultChildren,
  defaultClassName,
  defaultStyle,
  values,
}: RenderPropsHookOptions<T>) {
  return useMemo(() => {
    const children =
      callRenderProps(childrenProp, { ...values, defaultChildren }) ??
      defaultChildren;

    const className =
      callRenderProps(classNameProp, { ...values, defaultClassName }) ??
      defaultClassName;

    const style = callRenderProps(styleProp, { ...values, defaultStyle });

    return {
      ...(children != null ? { children } : {}),
      ...(className ? { className } : {}),
      ...(style || defaultStyle ? { style: { ...defaultStyle, ...style } } : {}),
    };
  }, [
    childrenProp,
    classNameProp,
    styleProp,
    defaultChildren,
    defaultClassName,
    defaultStyle,
    values,
  ]);
}