@emotion/styledで、Material UIのThemeを利用 & 型推論できるようにする
環境
React.js : 16.12.0
Material UI : 4.9.5
@emotion/styled : 10.0.27
TypeScript : 3.7.2
前提
一応、前提としてはMaterial-UIのThemeをカスタマイズしていること。
以下のようにMuiThemeProvider(もしくは@material-ui/core/styles/ThemeProvider)を利用しているはず。
code:App.tsx
import React from "react";
import {
StylesProvider,
MuiThemeProvider,
createMuiTheme
} from "@material-ui/core";
const theme = createMuiTheme({ ... });
function App() {
return (
<StylesProvider injectFirst>
<MuiThemeProvider theme={theme}>
...
</MuiThemeProvider>
</StylesProvider>
);
}
export default App;
emotion-themingのインストール
EmotionのテーマをMaterial-UIのThemeに差し替えるためには、emotion-themingをインストールする。
code:bash
$ yarn add emotion-theming
ThemeProviderを追加
以下の要領で、emotionのThemeProviderをMuiThemeProviderの直下に入れ、theme引数に元々定義していたThemeインスタンスを渡す。一応、emotionのThemeProviderと分かるように EmotionThemeProviderとしている。
code:App.tsx
import React from "react";
import {
StylesProvider,
MuiThemeProvider,
createMuiTheme
} from "@material-ui/core";
+ import { ThemeProvider as EmotionThemeProvider } from "emotion-theming";
const theme = createMuiTheme({ ... });
function App() {
return (
<StylesProvider injectFirst>
<MuiThemeProvider theme={theme}>
+ <EmotionThemeProvider theme={theme}>
...
+ </EmotionThemeProvider>
</MuiThemeProvider>
</StylesProvider>
);
}
export default App;
これでMaterial-UIのThemeがEmotionにも適用された。
styled関数にTheme型をつける
@emotion/styledを利用した時に、themeが型推論されるようにstyled関数の定義を上書きする。
※型推論というと微妙で、一応Themeのプロパティは補完されるようになる。(ただし、anyという...)
code:index.d.ts
declare module '@emotion/styled' {
import { CreateStyled } from '@emotion/styled/types/index';
import { Theme } from '@material-ui/core';
export * from '@emotion/styled/types/index';
const customStyled: CreateStyled<Theme>;
export default customStyled;
}
@emotion/styledの型定義を見れば分かる通り、styledの実態はCreateStyledインターフェースである。
code:@emotion/styled/types/index.d.ts
export interface CreateStyled<Theme extends object = any>
extends BaseCreateStyled<Theme>,
StyledTags<Theme> {}
declare const styled: CreateStyled
export default styled
なので、CreateStyled側のThemeをMaterial-UIのThemeで上書けば良い。
これでOK。
code:TypeScript
import styled from '@emotion/styled';
const StyledButton = styled(Button)(props => ({
backgroundColor: props.theme.palette.primary.main,
}));
Storybookを利用している場合は、decoratorでテーマの設定を行う
code:.storybook/preview.js
import React from "react";
import { addDecorator } from "@storybook/react";
import ThemeProvider from "../src/customProvider/ThemeProvider";
addDecorator(storyFn => <ThemeProvider>{storyFn()}</ThemeProvider>);
App.tsxに定義していたThemeProviderの設定を、src/customProvider/ThemeProvider.tsxに移してStorybookでも参照できるようにした。