material-ui
material-ui copied to clipboard
[material-ui] Avoid `ownerState` silent propagation
Following-up on https://github.com/mui/material-ui/pull/42062#discussion_r1592946897
Problem
With the standardization of the slots pattern, we've run into this multiple times:
- Parent component has
ownerState
, let's call itownerStateParent
- Child slot component has it's own
ownerState
, let's call itownerStateChild
, that it needs to provide to its own children
The ownerStateParent
is provided to the child slot component, as it's useful when overriding.
But the child needs to take care of two things:
- That the
ownerStateParent
, coming through props, doesn't override its ownownerStateChild
when passing it to its own children. This is the reason for the change on lines 118-123:ownerState={ownerState}
must come after{...other}
. - That the
ownerStateParent
is not nested inside its ownownerStateChild
, otherwise, it would overrideownerStateChild
because of howcreateStyled
merges props (reference). This could cause bugs when accessing theownerState
in style variants props matching. Like it's done in this component (line 68). This is the reason fordelete ownerState.ownerState
on this file.
The pattern of spreading other
into the root component, as well as spreading props
into ownerState
, is spread throughout the codebase, and that's why we've been bumping into this repeatedly. This behavior is error-prone, as it's not evident unless you're familiar with the codebase.
Ideas
- It might be worth it to invest in a shared mechanism that all components can rely on to safely spread
other
and/orprops
without propagating unwantedownerState
s. - Never inherit other component which turns out to be easier to maintain. Always inherit styled-component (to get styles) from another component instead of wrapping the whole component. (reference)
- Abstract this inside the
useSlot
hook. The problem with this one is that most components would need to useuseSlot
.
Search keywords:
@DiegoAndai From my point of view, another idea could be to delete props.ownerState in the useThemeProps hook or in every component before the props are spreaded. This should solve the problem with ownerState.ownerState and also that the parentOwnerState appears in ...other or ...props. I find it difficult to handle the whole thing in the useSlot hook, as the ownerState would no longer be passed to styled components if I'm not wrong.