はじめに
styled-componentsで条件付きレンダリングをする場合、以下のように、項目ごとに場合分けをする例をよく見ます
基本はこれで問題ないのですが、場合分けの種類が増えて来ると、管理が面倒になります。
この場合、場合分けの条件ごとにclassのようにスタイリングをまとめたいと思うのが自然だと思います。
そこで、複数の項目をclassの有無のように一気に設定した場合についてわからなかったので、調べました。
const Wrapper = styled.div.attrs((props) => ({
...props,
}))`
background: ${(props) => (props.hoge ? '#fff' : '#000')};
`
やりたいこと
以下のようにpropsの条件一つでスタイリングが大きく変わる場合を想定します。
上の場合と下の場合で、個別にcssを管理することを目標とします。
まずはデフォルトの状態を準備
まずは、props.typeに何も指定していない、アニメーションのない状態を実装します。
import React, { VFC } from "react";
import styled from "styled-components";
export type ButtonProps = {
className?: string;
type?: "bgleft" | "bgright";
};
const Button: VFC<ButtonProps> = ({ className, type }: ButtonProps) => {
return (
<WrapperButton className={className} type={type}>
<span>ボタン</span>
</WrapperButton>
);
};
const WrapperButton = styled.a.attrs((props) => ({ ...props }))`
/* default */
position: relative;
overflow: hidden;
text-decoration: none;
display: inline-block;
border: 1px solid #555;
padding: 10px 30px;
text-align: center;
outline: none;
transition: ease 0.2s;
span {
position: relative;
z-index: 3;
color: #333;
}
:hover {
span {
color: #fff;
}
}
`;
export default Button;
MixInの定義
次に左右から動作するアニメーションをMixInで定義します。
最初に、styled-componenstからcssをインポートします。
import React, { VFC } from "react";
import styled, { css } from "styled-components"; //ここ
次にMixInを定義します。
それぞれ、左右から動作する部分のcssです。
const MixInBgLeft = css`
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
z-index: 2;
background-color: #333;
width: 100%;
height: 100%;
transition: transform 0.6s cubic-bezier(0.8, 0, 0.2, 1) 0s;
transform: scale(0, 1);
transform-origin: right top;
}
&:hover:before {
transform-origin: left top;
transform: scale(1, 1);
}
`;
const MixInBgRight = css`
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
z-index: 2;
background-color: #333;
width: 100%;
height: 100%;
transition: transform 0.6s cubic-bezier(0.8, 0, 0.2, 1) 0s;
transform: scale(0, 1);
transform-origin: left top;
}
&:hover:before {
transform-origin: right top;
transform: scale(1, 1);
}
`;
条件付きでレンダリング
最後にstyledの中にMixInを条件付きで追加します。
const WrapperButton = styled.a.attrs((props) => ({ ...props }))`
//....省略
/* typeにbgleftを選択した場合 */
${(props) => props.type === "bgleft" && MixInBgLeft}
/* typeにbgrightを選択した場合 */
${(props) => props.type === "bgright" && MixInBgRight}
`;
このbuttonを使う側は以下のようにして使います。
import Button from '***'
<Button type="bgleft" />
// or
<Button type="bgright" />
最後に
今回の例だと、最初に紹介した、項目ごとに場合分けする場合でもよかったかも知れませんが、props.typeが増える場合を考えると、このような方ほでの実装も悪くはないかも知れません。
もっと良い方法があればぜひ共有してください。
最後に全部まとめたコードを貼ります。
import React, { VFC } from "react";
import styled, { css } from "styled-components";
export type ButtonProps = {
className?: string;
type?: "bgleft" | "bgright";
};
const Button: VFC<ButtonProps> = ({ className, type }: ButtonProps) => {
return (
<WrapperButton className={className} type={type}>
<span>ボタン</span>
</WrapperButton>
);
};
const MixInBgLeft = css`
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
z-index: 2;
background-color: #333;
width: 100%;
height: 100%;
transition: transform 0.6s cubic-bezier(0.8, 0, 0.2, 1) 0s;
transform: scale(0, 1);
transform-origin: right top;
}
&:hover:before {
transform-origin: left top;
transform: scale(1, 1);
}
`;
const MixInBgRight = css`
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
z-index: 2;
background-color: #333;
width: 100%;
height: 100%;
transition: transform 0.6s cubic-bezier(0.8, 0, 0.2, 1) 0s;
transform: scale(0, 1);
transform-origin: left top;
}
&:hover:before {
transform-origin: right top;
transform: scale(1, 1);
}
`;
const WrapperButton = styled.a.attrs((props) => ({ ...props }))`
/* default */
position: relative;
overflow: hidden;
text-decoration: none;
display: inline-block;
border: 1px solid #555;
padding: 10px 30px;
text-align: center;
outline: none;
transition: ease 0.2s;
span {
position: relative;
z-index: 3;
color: #333;
}
:hover {
span {
color: #fff;
}
}
/* typeにbgleftを選択した場合 */
${(props) => props.type === "bgleft" && MixInBgLeft}
/* typeにbgrightを選択した場合 */
${(props) => props.type === "bgright" && MixInBgRight}
`;
export default Button;
↧