はじめに
前回の記事では、styled-componentsの超基礎的なところを扱いました。
あまりstyled-componentsについて詳しく知らなかったのですが、ちゃんと調べてみるとかなり使い勝手がいいので、個人的にだいぶ好きになってきています。
本記事では、まだまだ使い所があるstyled-componentsの機能について紹介します。
何にでもstyledを適用する
味気ないコンポーネント
前回はstyled
でスタイルを適用したコンポーネントを作っていました。
しかし、アプリ開発は自作のコンポーネントだけでは成り立ちません。
例えば、ReactRouterのLink
コンポーネントを使うことを考えてみます。
※ReactRouterについてはこちらの拙記事を参考にしてください。
以下のようなコードを定義します。
import { Link } from "react-router-dom";
const App = () => {
return (
<>
<h1>App</h1>
<Link to="/other">otherへ</Link>
</>
);
};
export default App;
その他に定義すべきコードは以下の折りたたみから確認してください。
App.tsx以外のソースコードを確認
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import Other from "./Other";
import {
RouteObject,
RouterProvider,
createBrowserRouter,
} from "react-router-dom";
const routes: RouteObject[] = [
{
path: "/",
element: <App />,
},
{
path: "/other",
element: <Other />,
},
];
const router = createBrowserRouter(routes);
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
const Other = () => {
return <h1>Other</h1>;
};
export default Other;
Link
コンポーネントで生成されるリンクは少し味気ないですね。
もっと自由にスタイルを定義したいときstyled
を適用できます。
サードパーティのコンポーネントにスタイルを適用
やりかたはスタイルの継承と一緒です。
import { Link } from "react-router-dom";
import styled from "styled-components";
const CustomizeLink = styled(Link)`
text-decoration: none;
border: 2px solid gray;
padding: 5px;
border-radius: 10px;
color: black;
font-size: 20px;
`;
const App = () => {
return (
<>
<h1>App</h1>
<CustomizeLink to="/other">otherへ</CustomizeLink>
</>
);
};
export default App;
おしゃれかはわかりませんが、味気ないリンクに装飾を施すことができました。
このように、サードパーティのコンポーネントにでもstyled
を使ってスタイルを適用することができます。
もちろん、自作のコンポーネントでも可能です。
スタイル適用の条件
ただ、実際は何にでもというわけではなく1つだけ条件があります。
それは「propsとしてclassName
を受け取れる」ということです。
styled-componentsは描画の際に自動でクラスを付与してスタイルを適用しています。
そのため、className
を設定できる必要があります。
それだけ満たせばどんなコンポーネントにでもスタイルを適用できるため、自由度が高くなります。
追加のpropsの定義
共通の属性を定義する
外部リンクを扱う場合、セキュリティのためにtarget="_blank"とrel="noopener noreferrer"を設定することが推奨されています。
これらの属性を毎回手動で設定するのは面倒ですし、忘れる可能性もあります。
.attrsを使用することで、これらの属性を自動的に適用できます。
import styled from 'styled-components';
import { Link } from 'react-router-dom';
const ExternalLink = styled.a.attrs({
target: '_blank',
rel: 'noopener noreferrer',
})`
color: #0077cc;
text-decoration: none;
&:hover {
text-decoration: underline;
}
`;
const StyledRouterLink = styled(Link)`
color: #0077cc;
text-decoration: none;
&:hover {
text-decoration: underline;
}
`;
const App = () => {
return (
<div>
<h1>Welcome to my site</h1>
<ExternalLink href="https://www.example.com">External Link</ExternalLink>
<br />
<StyledRouterLink to="/about">About Page</StyledRouterLink>
</div>
);
};
export default App;
この例では、ExternalLinkコンポーネントを使用するたびに、自動的にtargetとrel属性が設定されます。
また、StyledRouterLinkコンポーネントは内部リンク用のスタイリングを提供します。
これにより、一貫性のあるリンクスタイルを保ちつつ、適切な属性設定を確実に行うことができます。
複雑な属性の計算
フォームのバリデーションを考えてみましょう。入力フィールドにエラーがある場合、アクセシビリティのためにaria-invalidとaria-describedby属性を設定する必要があります。これらの属性は動的に変更される可能性が高いため、.attrsを使用して効果的に管理できます。
import React, { useState } from 'react';
import styled from 'styled-components';
const Input = styled.input.attrs<{ $hasError?: boolean }>(props => ({
'aria-invalid': props.$hasError,
'aria-describedby': props.$hasError ? `${props.id}-error` : undefined,
}))`
border: 2px solid ${props => props.$hasError ? 'red' : '#ccc'};
padding: 8px;
margin-bottom: 5px;
`;
const ErrorMessage = styled.span`
color: red;
font-size: 0.8em;
`;
const FormField = ({ id, label, value, onChange, error }) => {
return (
<div>
<label htmlFor={id}>{label}</label>
<Input
id={id}
value={value}
onChange={onChange}
$hasError={!!error}
/>
{error && <ErrorMessage id={`${id}-error`}>{error}</ErrorMessage>}
</div>
);
};
const App = () => {
const [email, setEmail] = useState('');
const [emailError, setEmailError] = useState('');
const handleEmailChange = (e) => {
const value = e.target.value;
setEmail(value);
if (!value.includes('@')) {
setEmailError('有効なメールアドレスを入力してください');
} else {
setEmailError('');
}
};
return (
<div>
<h1>Contact Form</h1>
<FormField
id="email"
label="Email:"
value={email}
onChange={handleEmailChange}
error={emailError}
/>
</div>
);
};
export default App;
この例では、Inputコンポーネントに.attrsを使用して、エラー状態に応じてaria-invalidとaria-describedby属性を動的に設定しています。
また、エラーメッセージの表示も制御しています。
これにより、アクセシビリティに配慮しつつ、ユーザーフレンドリーなフォームを簡単に作成できます。
まとめ
styled-componentsについて、より便利に使うことのできる機能をいくつか紹介しました。
単にスタイルを当てるだけではなく、外部からpropsを動的に変更したり、自由にスタイルを適用したりすることができました。
cssファイルを外部に切り出して、スタイルを定義するよりも汎用性が高く、便利に使えるものなのでは?と感じました。
今後も色々便利なモジュールの使い方を紹介していければと思います。