styled-componentsの利用時に拡張がうまくいかなったのが解決できたので備忘録です
失敗するコード
- 拡張したいコンポーネントを用意します
- このコンポーネントは外部からの拡張に失敗します
import React from 'react';
import styled from 'styled-components';
type Props = {
children: string;
};
const Component = styled.span``;
const Text: React.FC<Props> = (props) => {
return (
<Component>{props.children}</Component>
);
}
export default Text;
- このコンポーネントの拡張に失敗するコード(実際には失敗していない)
const TextExtended = styled(Text)`
color: #137a7f;
`;
TextExtendedの内容を確認する
- TextExtendedをJSON.stringifyで出力してみます
- componentStyleというプロパティにcolorの指定があるのがわかります
{
"displayName": "Styled(Text)",
"attrs": [],
"componentStyle": {
"rules": [
"\n color: #137a7f;\n"
],
"isStatic": false,
"componentId": "sc-EHOje",
"lastClassName": "biMEjp"
},
"foldedComponentIds": [],
"styledComponentId": "sc-EHOje"
}
- ここでlastClassNameというプロパティに着目します
- ChromeのデベロッパーツールでTextコンポーネントの出力結果の要素にlastClassNameを追加すると無事に拡張したはずのスタイルが適用されることがわかります
- 上記の検証からlastClassNameをTextコンポーネントのクラスに追加できればよいことがわかります
- 次にlastClassNameを毎回取り出すのは現実的ではないので、Textコンポーネントが受け取ったpropsを確認します
- classNameを受け取ってくれていればいいなという淡い期待
- Propsに指定していた型と異なりclassNameというプロパティが生えていることがわかります
- Propsの型を必要なものだけ指定するプロジェクトであればハマりやすい罠です
{
"children": "TEST",
"className": "sc-EHOje biMEjp"
}
解決方法
- ここまでの流れでclassNameをTextコンポーネントに追加すればよいことがわかったので、下記の様に修正を加えます
- classNameがTextコンポーネントに確実に渡る様にするため下記の様にします
import React from 'react';
import styled from 'styled-components';
type Props = {
children: string;
className?: string;
};
const Component = styled.span``;
const Text: React.FC<Props> = (props) => {
return (
<Component className={props.className}>{props.children}</Component>
);
}
export default Text;
- またはclassNameの指定をせずにpropsを全て渡す形でも拡張することが可能となります
import React from 'react';
import styled from 'styled-components';
type Props = {
children: string;
};
const Component = styled.span``;
const Text: React.FC<Props> = (props) => {
return (
<Component {...props}>{props.children}</Component>
);
}
export default Text;
まとめ
- styled-componentsは動的にクラスを追加して、そのクラスをコンポーネントに追加する形でスタイルを適用している
- コンポーネントへのクラスの追加はclassNameを用いて行われており、その値を捨ててしまうとスタイルの拡張に失敗してしまう
- TypeScriptで型を制限していても実行時に渡されるパラメータまで制御出来るわけではない