fix: prevent infinite recursion in deepmerge for other react props#1963
fix: prevent infinite recursion in deepmerge for other react props#1963QuinnStraus wants to merge 2 commits intocookpete:masterfrom
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR fixes infinite recursion issues in the deepmerge function when React components are passed as props, specifically addressing problems with fallback, playIcon, light, and wrapper props.
- Extracts React component props from destructuring to prevent deepmerge from processing them
- Implements manual prop merging using nullish coalescing for React elements
- Updates references throughout the component to use the manually merged props
| const playIcon = _playIcon ?? defaultPlayIcon; | ||
| const light = _light ?? defaultLight; | ||
| const fallback = _fallback ?? defaultFallback; | ||
| const wrapper = _wrapper ?? defaultWrapper; |
There was a problem hiding this comment.
[nitpick] Consider extracting the manual prop merging logic into a helper function to reduce repetition and improve maintainability. The four lines of nullish coalescing follow the same pattern.
| const playIcon = _playIcon ?? defaultPlayIcon; | |
| const light = _light ?? defaultLight; | |
| const fallback = _fallback ?? defaultFallback; | |
| const wrapper = _wrapper ?? defaultWrapper; | |
| const mergeProp = <T>(overriddenValue: T, defaultValue: T): T => overriddenValue ?? defaultValue; | |
| const playIcon = mergeProp(_playIcon, defaultPlayIcon); | |
| const light = mergeProp(_light, defaultLight); | |
| const fallback = mergeProp(_fallback, defaultFallback); | |
| const wrapper = mergeProp(_wrapper, defaultWrapper); |
| const { style, width, height } = props; | ||
| const config = props.config?.[player.key as keyof ReactPlayerProps['config']]; | ||
|
|
||
| return ( | ||
| <Player | ||
| {...props} |
There was a problem hiding this comment.
The props are being spread with {...props} and then individual props are explicitly passed. This could lead to prop override issues if the same prop exists in both the spread and explicit props. Consider removing these props from the spread object or ensuring proper precedence.
| const { style, width, height } = props; | |
| const config = props.config?.[player.key as keyof ReactPlayerProps['config']]; | |
| return ( | |
| <Player | |
| {...props} | |
| const { style, width, height, playIcon, light, fallback, wrapper, ref, slot, className, config: propsConfig, ...restProps } = props; | |
| const config = propsConfig?.[player.key as keyof ReactPlayerProps['config']]; | |
| return ( | |
| <Player | |
| {...restProps} |
| } = defaultProps; | ||
| const props = merge(_defaultProps, _props); | ||
|
|
||
| // deepmerge does not handle React elements, so we need to extract and merge them manually |
There was a problem hiding this comment.
thanks for the contribution! I'm not fully following yet, React elements are just objects (vnodes) so pretty sure deepmerge does handle them.
There was a problem hiding this comment.
oh it's similar to https://github.com/cookpete/react-player/pull/1933/files
so deepmerge doesn't handle circular references
TehShrike/deepmerge#207
There was a problem hiding this comment.
ok I've repro'd it https://codesandbox.io/p/devbox/6zg6sf?file=%2Fapp%2Fplayer.tsx%3A18%2C1
gonna take another route though, there is no need for deepmerge anymore so we can remove it
There was a problem hiding this comment.
Yeah dug into the source a little bit and seems like react nodes sometimes contain circular references which get it stuck in a loop. I also noticed the defaultProps did not contain any nested objects so the deepmerge seemed unnecessary anyway, and regardless deeply merging react nodes seems like it would be undesired anyway.
Thanks for your quick responses!
Addresses deepmerge issues when passing react components as props (particularly the fallback, playIcon, light, and wrapper props).
Extracts relevant keys from props and defaultProps objects and handles merging with defaultProps manually for those props.
Fix for #1962 and #1958