TS와 함께 감정 JS를 사용하여 재료 UI 4 makeStyles 및 withStyles를 재료 UI 5로 업그레이드

감정을 사용하여 mui5로 업그레이드할 때 매우 멋진 블로그를 발견했습니다.
그러나 이 구현에는 TS 지원, withStyles Styled 구성 요소를 처리하는 방법과 같이 몇 가지 부족한 점이 있습니다.
이 블로그 게시물에서는 누락된 항목에 대해 언급하겠습니다.

Styles Root 부분은 위의 블로그에서 언급한 것과 동일합니다.

감정 테마 선언



import { Theme as MuiTheme } from '@mui/material/styles'
import '@emotion/react'

declare module '@emotion/react' {
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
  export interface Theme extends MuiTheme {}
}

TS를 지원하는 사용자 지정 후크



import { useMemo } from 'react'
import { css, CSSInterpolation } from '@emotion/css'
import { useTheme } from '@emotion/react'
import { Theme as MuiTheme } from '@mui/material/styles'

function useEmotionStyles(
  styles: () => Record<string, CSSInterpolation>
): Record<string, ReturnType<typeof css>>

function useEmotionStyles(
  styles: (theme: MuiTheme) => Record<string, CSSInterpolation>
): Record<string, ReturnType<typeof css>>

function useEmotionStyles<T>(
  styles: (theme: MuiTheme, props: T) => Record<string, CSSInterpolation>,
  props: T
): Record<string, ReturnType<typeof css>>

function useEmotionStyles<T>(
  styles: (theme: MuiTheme, props?: T) => Record<string, CSSInterpolation>,
  props?: T
): Record<string, ReturnType<typeof css>> {
  const theme = useTheme()
  return useMemo(() => {
    const classes = styles(theme, props)
    const classNameMap = {}

    Object.entries(classes).forEach(([key, value]) => {
      classNameMap[key] = css(value)
    })

    return classNameMap
  }, [props, styles, theme])
}

export default useEmotionStyles

여기에 후크에 대한 가능한 호출에 대한 오버로드된 후크가 있습니다.

간단한 예는 다음과 같습니다.

type GridProps = { itemMargin: number | string }

const gridStyles = (theme: Theme, { itemMargin }: GridProps) => ({
  container: {
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    flexWrap: 'wrap' as CSSTypes.Property.FlexWrap,
    maxWidth: theme.breakpoints.values.md,
    [theme.breakpoints.down('sm')]: {
      maxWidth: 420
    },
    '&>*': {
      margin: itemMargin
    }
  }
})


const Component = () => {
  const { container } = useEmotionStyles<GridProps>(gridStyles, { itemMargin })

  return (
    <Container className={container}>
  )
}


감정을 사용하여 키프레임 애니메이션을 구현하려는 경우 이 방법을 사용할 수 있습니다.

import { css, keyframes } from '@emotion/react'

const fadeIn = keyframes({
  '0%': {
    opacity: 0
  },
  '100%': {
    opacity: 1
  }
})
const styles = () => ({
  text: css({
    display: 'flex',
    alignItems: 'center',
    animation: `${fadeIn} 2s`
  })
})



스타일이 지정된 구성 요소에 대한 사용자 지정 후크(withStyles 대체)




import React, { useMemo } from 'react'
import { Theme, useTheme } from '@emotion/react'
import { Theme as MuiTheme } from '@mui/material/styles'
import styled, { StyledComponent } from '@emotion/styled/macro'
import { CSSInterpolation } from '@emotion/css'
import {
  OverridableComponent,
  OverridableTypeMap
} from '@mui/material/OverridableComponent'

type ReturnedType<T extends ComponentType> = StyledComponent<
  JSX.LibraryManagedAttributes<T, React.ComponentProps<T>> & {
    theme?: Theme
  }
>

type ComponentType =
  | OverridableComponent<OverridableTypeMap>
  | React.JSXElementConstructor<JSX.Element>
  | ((props?: React.ComponentProps<any>) => JSX.Element)

function useEmotionStyledComponent<T extends ComponentType>(
  styles: () => Record<string, CSSInterpolation>,
  WrappedComponent: T
): ReturnedType<T>

function useEmotionStyledComponent<T extends ComponentType>(
  styles: (theme: MuiTheme) => Record<string, CSSInterpolation>,
  WrappedComponent: T
): ReturnedType<T>

function useEmotionStyledComponent<T extends ComponentType, R>(
  styles: (theme: MuiTheme, props: R) => Record<string, CSSInterpolation>,
  WrappedComponent: T,
  props: R
): ReturnedType<T>

function useEmotionStyledComponent<T extends ComponentType, R>(
  styles: (theme: MuiTheme, props?: R) => Record<string, CSSInterpolation>,
  WrappedComponent: T,
  props?: R
): ReturnedType<T> {
  const theme = useTheme()
  return useMemo(() => {
    const strings = styles(theme, props)

    return styled(WrappedComponent)(strings?.root)
  }, [WrappedComponent, props, styles, theme])
}

export default useEmotionStyledComponent



이 후크를 사용하려면 스타일에 루트 요소가 하나만 있어야 하며 모든 스타일이 그 안에 있어야 합니다.

const StyledDialog = (props: DialogProps) => {
  const Component = useEmotionStyledComponent<typeof Dialog>(
    (theme: Theme) => ({
      root: {
        '& div.MuiDialog-container': {
          height: 'auto'
        },
        '& div.MuiDialog-paper': {
          alignItems: 'center',
          padding: theme.spacing(0, 2, 2, 2),
          minWidth: 240
        }
      }
    }),
    Dialog
  )
  return <Component {...props} />
}

const MenuButton = (props: FabProps) => {
  const StyledMenuButton = useEmotionStyledComponent<typeof Fab, FabProps>(
    (theme: Theme) => ({
      root: {
        position: 'fixed',
        top: theme.spacing(2),
        left: theme.spacing(4)
      }
    }),
    Fab,
    props
  )
  return <StyledMenuButton {...props} />
}


이 구성 요소를 Styled 구성 요소로 사용합니다.

이 두 개의 사용자 지정 후크를 사용하여 makeStyles 및 withStyles를 대체할 수 있습니다. 질문이 있으면 알려주세요.

좋은 웹페이지 즐겨찾기