From cee9e218ff82543e9c04841a2645d149248097af Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Tue, 30 Jul 2024 10:37:08 -0400 Subject: [PATCH] fix: make theme props more stable --- .../src/useThemeProps/useThemeProps.js | 4 +- .../src/resolveProps/resolveProps.ts | 77 +++++++++++-------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/mui-system/src/useThemeProps/useThemeProps.js b/packages/mui-system/src/useThemeProps/useThemeProps.js index d7c98f380eee85..08033ec2ca0c94 100644 --- a/packages/mui-system/src/useThemeProps/useThemeProps.js +++ b/packages/mui-system/src/useThemeProps/useThemeProps.js @@ -1,4 +1,5 @@ 'use client'; +import * as React from 'react'; import getThemeProps from './getThemeProps'; import useTheme from '../useTheme'; @@ -7,6 +8,5 @@ export default function useThemeProps({ props, name, defaultTheme, themeId }) { if (themeId) { theme = theme[themeId] || theme; } - const mergedProps = getThemeProps({ theme, name, props }); - return mergedProps; + return React.useMemo(() => getThemeProps({ theme, name, props }), [theme, name, props]); } diff --git a/packages/mui-utils/src/resolveProps/resolveProps.ts b/packages/mui-utils/src/resolveProps/resolveProps.ts index fef2dbdb86f049..81b29f67526e34 100644 --- a/packages/mui-utils/src/resolveProps/resolveProps.ts +++ b/packages/mui-utils/src/resolveProps/resolveProps.ts @@ -1,8 +1,10 @@ +/* eslint-disable no-restricted-syntax */ + /** * Add keys, values of `defaultProps` that does not exist in `props` - * @param {object} defaultProps - * @param {object} props - * @returns {object} resolved props + * @param defaultProps + * @param props + * @returns resolved props */ export default function resolveProps< T extends { @@ -14,36 +16,51 @@ export default function resolveProps< >(defaultProps: T, props: T) { const output = { ...props }; - (Object.keys(defaultProps) as Array).forEach((propName) => { - if (propName.toString().match(/^(components|slots)$/)) { - output[propName] = { - ...(defaultProps[propName] as any), - ...(output[propName] as any), - }; - } else if (propName.toString().match(/^(componentsProps|slotProps)$/)) { - const defaultSlotProps = (defaultProps[propName] || {}) as T[keyof T]; - const slotProps = props[propName] as {} as T[keyof T]; - output[propName] = {} as T[keyof T]; + for (const key in defaultProps) { + if (Object.prototype.hasOwnProperty.call(defaultProps, key)) { + const propName = key as keyof T; + + if (propName === 'components' || propName === 'slots') { + output[propName] = { + ...(defaultProps[propName] as any), + ...(output[propName] as any), + }; + } else if (propName === 'componentsProps' || propName === 'slotProps') { + const defaultSlotProps = defaultProps[propName] as T[keyof T] | undefined; + const slotProps = props[propName] as {} as T[keyof T] | undefined; + + if (!slotProps || isObjectEmpty(slotProps)) { + // Reduce the iteration if the slot props is empty + output[propName] = defaultSlotProps || ({} as T[keyof T]); + } else if (!defaultSlotProps || isObjectEmpty(defaultSlotProps)) { + // Reduce the iteration if the default slot props is empty + output[propName] = slotProps; + } else { + output[propName] = { ...slotProps }; - if (!slotProps || !Object.keys(slotProps)) { - // Reduce the iteration if the slot props is empty - output[propName] = defaultSlotProps; - } else if (!defaultSlotProps || !Object.keys(defaultSlotProps)) { - // Reduce the iteration if the default slot props is empty - output[propName] = slotProps; - } else { - output[propName] = { ...slotProps }; - Object.keys(defaultSlotProps).forEach((slotPropName) => { - (output[propName] as Record)[slotPropName] = resolveProps( - (defaultSlotProps as Record)[slotPropName], - (slotProps as Record)[slotPropName], - ); - }); + for (const slotKey in defaultSlotProps) { + if (Object.prototype.hasOwnProperty.call(defaultSlotProps, slotKey)) { + const slotPropName = slotKey; + (output[propName] as Record)[slotPropName] = resolveProps( + (defaultSlotProps as Record)[slotPropName], + (slotProps as Record)[slotPropName], + ); + } + } + } + } else if (output[propName] === undefined) { + output[propName] = defaultProps[propName]; } - } else if (output[propName] === undefined) { - output[propName] = defaultProps[propName]; } - }); + } return output; } + +function isObjectEmpty(object: Object) { + // eslint-disable-next-line + for (const _ in object) { + return false; + } + return true; +}