Skip to content

useSlottedContext should not throw when slot is not found #9283

@unional

Description

@unional

Provide a general summary of the issue here

Referring to this code:

if (!ctx.slots[slotKey]) {
  let availableSlots = new Intl.ListFormat().format(Object.keys(ctx.slots).map(p => `"${p}"`));
  let errorMessage = slot ? `Invalid slot "${slot}".` : 'A slot prop is required.';

  throw new Error(`${errorMessage} Valid slot names are ${availableSlots}.`);
}

https://github.com/adobe/react-spectrum/blob/main/packages/react-aria-components/src/utils.tsx#L171-L176

This violates Open/Close principle, so that it hinders the ability to compose and add new slots in extended components.

Using Button as example, I want to add a clear slot and use the button to clear the text in a input field:

<InputField>
  <ClearButton>
</InputField>
export function ClearButton() {
  return <StyledButton slot="clear">X</StyledButton>
}
export const StyledButtonContext = createContext<ContextValue<...>>(null)

export const StyledButton = forwardRef(props, ref) {
  ;[props, ref] = useContextProps(props, ref, ButtonContext]
  ;[props, ref] = useContextProps(props, ref, StyledButtonContext]

  return <Button ref={ref} {...props} style={{...}}/>
}
export function InputField({ children }) {
  return (
    <StyledButtonContext value={{ slots: { clear: { onPress: ... } } }}>
      {children}
    </StyledButtonContext>
  )
}

Since the props { slot: 'clear' } is eventually passed to useSlottedContext, it throws.

Workaround this requires manually picking out slot: 'clear' in the StyledButton, breaking encapsulation and OCP.

Something like:

export const StyledButton = forwardRef(props, ref) {
  const { slot, ...rest } = props
  
  let baseButtonProps = slot === 'clear' ? rest : props

  ;[props, ref] = useContextProps(baseButtonProps, ref, ButtonContext]
  ;[props, ref] = useContextProps({ slot, ...props}, ref, StyledButtonContext]

  baseButtonProps = slot === 'clear' ? rest : props
  
  return <Button ref={ref} {...baseButtonProps} style={{...}}/>
}

You can see this is not scalable as more slots are used in the system.

🤔 Expected Behavior?

return undefined

😯 Current Behavior

throw error

💁 Possible Solution

No response

🔦 Context

See above

🖥️ Steps to Reproduce

Hopefully the example above should be clear enough.

Version

1.13.0

What browsers are you seeing the problem on?

Chrome

If other, please specify.

No response

What operating system are you using?

macos

🧢 Your Company/Team

Palo Alto Networks

🕷 Tracking Issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions