0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

前回の記事では、styled-componentsの超基礎的なところを扱いました。
あまりstyled-componentsについて詳しく知らなかったのですが、ちゃんと調べてみるとかなり使い勝手がいいので、個人的にだいぶ好きになってきています。

本記事では、まだまだ使い所があるstyled-componentsの機能について紹介します。

何にでもstyledを適用する

味気ないコンポーネント

前回はstyledでスタイルを適用したコンポーネントを作っていました。

しかし、アプリ開発は自作のコンポーネントだけでは成り立ちません。
例えば、ReactRouterのLinkコンポーネントを使うことを考えてみます。
※ReactRouterについてはこちらの拙記事を参考にしてください。

以下のようなコードを定義します。

App.tsx
import { Link } from "react-router-dom";

const App = () => {
  return (
    <>
      <h1>App</h1>
      <Link to="/other">otherへ</Link>
    </>
  );
};

export default App;

その他に定義すべきコードは以下の折りたたみから確認してください。

App.tsx以外のソースコードを確認
main.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>
);
Other.tsx
const Other = () => {
  return <h1>Other</h1>;
};

export default Other;

画面は以下のようになります。

Linkコンポーネントで生成されるリンクは少し味気ないですね。
もっと自由にスタイルを定義したいときstyledを適用できます。

サードパーティのコンポーネントにスタイルを適用

やりかたはスタイルの継承と一緒です。

App.tsx(スタイル追加)
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ファイルを外部に切り出して、スタイルを定義するよりも汎用性が高く、便利に使えるものなのでは?と感じました。

今後も色々便利なモジュールの使い方を紹介していければと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?