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?

ReactでのStyleのあてかた

Last updated at Posted at 2025-02-02

■はじめに

最近、Reactを学ぶ中で、スタイルの当て方が複数あったり、CSSとは書き方が違ったりと学びがあったのでまとめます。

■インラインスタイルをあてる方法

◆インラインスタイル

App.jsx
function App() {
  return (
    <>
      <button>ボタン</button>
      <div style={{ color: 'red', fontSize: 20 }}>クリックされました</div>
    </>
  )
}

export default App

スクリーンショット 2025-01-21 8.04.43.png

インラインで、スタイルを当てるには、HTMLタグに対してstyle={}を書きます。
{}がJSXという、HTMLの中にJSを書くための構文になります。
{}の中に更に{}を書きます。
2つ目の{}はオブジェクトを表しています。
その中にスタイルを書いていきます。
CSSを書く場合、font-sizeと書きますが、ReactではfontSizeと大文字で書きます。
数値に関しても「20」と書くと、勝手に単位を補完してくれます。

App.jsx
import { useState } from "react"

const Example = () => {
    const [isSelected, setIsSelected] = useState(false);

    const clickHandler = () => setIsSelected(prev => !prev);

    return (
        <>
            <button onClick={clickHandler}>ボタン</button>
            <div style={{ color: "red", "font-size": "20px" }}>{isSelected && "クリックされました。"}</div>
        </>
    )
};

export default Example;

ちなみに"font-size": "20px"と見慣れた感じで書くこともできます。

◆スタイルをオブジェクトとして切り出す

App.jsx
function App() {
  const style = {
    width: '100px',
    height: '50px',
    border: 'none',
    borderRadius: '10px',
    backgroundColor: 'skyblue',
  }

  return (
    <>
      <button style={style}>ボタン</button>
      <div style={{ color: 'red', fontSize: 20 }}>クリックされました</div>
    </>
  )
}

export default App

変数を宣言して、その中にstyleを書くこともできる。

◆stateに応じて、動的にスタイルを変える

App.jsx
import { useState } from 'react';

function App() {
	const [isClick, setClick] = useState(false);

	const style = {
		width: '100px',
		height: '50px',
		border: 'none',
		borderRadius: '10px',
		backgroundColor: isClick ? 'pink' : 'skyblue',
	};

	const clickAction = () => {
		setClick((prev) => !prev);
	};

	return (
		<>
			<button style={style} onClick={clickAction}>
				ボタン
			</button>
			<div style={{ color: 'red', fontSize: 20 }}>{isClick ? 'クリックされました' : ''}</div>
		</>
	);
}

export default App;

isClickをstateで管理して、isClickの値に応じてスタイルを動的に変えることもできます。

◆インラインスタイルの問題点

  • ::before, ::after, :active などの疑似要素は使えない。
  • @media (min-width: 600px) {} など、メディアクエリは使えない。
  • インラインスタイルで当てると、優先度が高くなり、スタイルを上書きするのが難しくなる。
  • コンポーネントが再レンダリングされると、スタイルが再計算されるので、パフォーマンスが落ちる。
  • ボタンを複数作って、同じスタイルを当てるときに、毎回style={style}を書く必要がある。

■外部CSSファイルを使う方法

◆外部CSSファイルをimportする

App.jsx
import { useState } from 'react';

import './App.css';

function App() {
	const [isClick, setClick] = useState(false);

	const clickAction = () => {
		setClick((prev) => !prev);
	};

	return (
		<>
			<button className='btn' onClick={clickAction}>
				ボタン
			</button>
			<div className='text'>{isClick ? 'クリックされました' : ''}</div>
		</>
	);
}

export default App;
App.css
#root {
	display: flex;
	justify-content: center;
	align-items: center;
  flex-direction: column;
}

.btn {
	display: inline-block;
	width: 100px;
	height: 50px;
	border: none;
	border-radius: 10px;
	margin: 5px;
}

.text {
	color: 'red';
	font-size: '20px';
  text-align: center;
}

外部CSSファイルを使うには、jsxファイル内で、import './App.css';というようにファイルをimportして使います。
CSSも通常のCSSで書くため、インラインスタイルとは違って、width: 100px;などを文字列として
width: ‘100’と定義する必要はありません。

◆外部CSSファイルをimportする問題点

App.jsx
import { useState } from 'react';

import './App.css';
import SubButton from './components/SubButton';

function App() {
	const [isClick, setClick] = useState(false);

	const clickAction = () => {
		setClick((prev) => !prev);
	};

	return (
		<>
			<button className="btn" onClick={clickAction}>
				ボタン
			</button>
			<SubButton />
			<div className="text">{isClick ? 'クリックされました' : ''}</div>
		</>
	);
}

export default App;
App.css
#root {
	display: flex;
	justify-content: center;
	align-items: center;
	flex-direction: column;
}

.btn {
	display: inline-block;
	width: 100px;
	height: 50px;
	border: none;
	border-radius: 10px;
	margin: 5px;
}

.text {
	color: 'red';
	font-size: '20px';
}

先ほどと同じように、App.jsxApp.cssにボタンの表示とデザインを当てています。

SubButton.jsx
import './SubButton.css';

const SubButton = () => {
	return <button className="btn">サブボタン</button>;
};

export default SubButton;
SubButton.css
.btn {
	background-color: #4CAF50;
}

App.jsxに表示しているボタンと違うデザインを当てたサブボタンを作りたいと思い、
src/components/SubButton.jsxにサブボタンコンポーネントを作りました。
src/components/SubButton.cssでサブボタンにだけ適用したいという意図で背景色を指定しました。

スクリーンショット 2025-01-26 12.14.01.png

この状態で画面をリロードすると、「ボタン」にも背景色が適用されてしまいます。

このように、外部CSSファイルをimportすると、クラス名が被って意図しない要素にもデザインが当たるという問題点があります。
デザインが増えると、競合を調査したり、競合を考慮しながら開発するのが難しくなります。

■CSSモジュールを使ってスタイルを当てる

◆外部CSSファイルの問題点おさらい

外部CSSファイルをimportして使うと、クラス名が競合して、意図しない要素のデザインが当たるという事象がありました。
サブボタンだけを緑色にしようとすると、ボタンも緑になりました。
このような競合を防ぐ方法が、CSSモジュールです。

◆CSSモジュールを使ってみる

App.jsx
import { useState } from 'react';

import style from  './App.module.css';
import SubButton from './components/SubButton';

console.log(style);

function App() {
	const [isClick, setClick] = useState(false);

	const clickAction = () => {
		setClick((prev) => !prev);
	};

	return (
		<>
			<button className={style.btn} onClick={clickAction}>
				ボタン
			</button>
			<SubButton />
			<div className="text">{isClick ? 'クリックされました' : ''}</div>
		</>
	);
}

export default App;
src/App.jsx
App.module.css

.btn {
	display: inline-block;
	width: 100px;
	height: 50px;
	border: none;
	border-radius: 10px;
	margin: 5px;
}

.text {
	color: 'red';
	font-size: '20px';
}

ここで注目したいのはファイル名です。
拡張子が.module.cssになっています。
CSSモジュールを使う場合は、拡張子に.module.cssと書きます。

JSXファイル側では、import style from './App.module.css';の読み込んで変数に格納します。

スクリーンショット 2025-01-26 14.27.19.png

styleの中身を確認すると、オブジェクトになっており、プロパティの値に一意な値がセットされています。
この値をclassName={style.btn}のように指定して、要素のクラス名として適用します。

こうすることで、コンポーネントごとに一意なクラス名が適用されるため、同一クラス名に意図しないCSSが適用されることを防ぐことができます。

SubButton.jsx
import style from './SubButton.module.css';

const SubButton = () => {
	return <button className={style.btn}>サブボタン</button>;
};

export default SubButton;
SubButton.module.css
.btn {
	display: inline-block;
	width: 100px;
	height: 50px;
	border: none;
	border-radius: 10px;
	margin: 5px;
	background-color: #4caf50;
}

サブボタンコンポーネントのCSSファイルも、CSSモジュールに書き換えてみます。

スクリーンショット 2025-01-26 14.31.11.png

クラス名を確認すると、ボタンとサブボタンのクラス名が違う事がわかります。
違うクラス名が当たることで、デザインをコンポーネント事に分けることができています。

◆CSSモジュールの注意点

.btn {
	display: inline-block;
	width: 100px;
	height: 50px;
	border: none;
	border-radius: 10px;
	margin: 5px;
	background-color: #4caf50;
}

button {
	background-color: red;
}

スクリーンショット 2025-01-26 14.33.22.png

CSSモジュール内で、buttonというタグ名に直接スタイルを書くと、グローバルに適用されるCSSになるため、意図しないデザインが当たる可能性があります。

そのため、クラス名かid名にデザインを当てるようにしたほうがいいです。

■CSS-in-JSでスタイルを当てる

◆CSS-in-JSとは何か?

CSS-in-JSは、JSの中にCSSを書けるようにする技術です。
CSS-in-JSを実現するライブラリとして「Panda CSS」「styles-components」「Emotion」がなどあります。

◆styles-componentsをインストールする

npm install styled-components

まずは、npm install styled-componentsでインストールします。

App.jsx
import styled from "styled-components";

console.dir(styled);

JSXのファイル内で、styled-componentsをインストールして、変数に代入します。

今回はstyledという変数に代入しています。

スクリーンショット 2025-01-28 8.04.06.png

ログに出力してみると、stylesの中にプロパティと値として関数が設定されている事がわかります。

プロパティ名は、HTMLタグの数だけ存在しています。

◆styles-componentsを使ってみる

App.jsx
import { useState } from 'react';

import styled from 'styled-components';

const ButtonStyle = styled.button`
	display: inline-block;
	width: 100px;
	height: 50px;
	border: none;
	border-radius: 10px;
	margin: 5px;
	background-color: skyblue;
`;

const TextStyle = styled.div`
	color: red;
	font-size: 20px;
`;

function App() {
	const [isClick, setClick] = useState(false);

	const clickAction = () => {
		setClick((prev) => !prev);
	};

	return (
		<>
			<ButtonStyle onClick={clickAction}>ボタン</ButtonStyle>
			<TextStyle>{isClick ? 'クリックされました' : 'クリックされていません'}</TextStyle>
		</>
	);
}

export default App;

ButtonStyleTextStylestyled-componentsを使ったスタイルのあて方です。
styled.buttonとするとタグが生成されます。

スクリーンショット 2025-01-28 8.16.23.png

実際にタグを見てみると、タグが生成されています。
このタグに一意なクラス名が当てられていることも確認できます。
一意なクラス名が当てられるため、インラインCSSや外部CSSファイルを読み込むパターンと比較して、CSSの競合が起こらないというメリットがあります。

◆動的にスタイルを変える方法

App.jsx
import { useState } from 'react';

import styled from 'styled-components';

const ButtonStyle = styled.button`
	display: inline-block;
	width: 100px;
	height: 50px;
	border: none;
	border-radius: 10px;
	margin: 5px;
	background-color: ${(props) => (props.$isClick ? 'skyblue' : 'green')};
`;

const TextStyle = styled.div`
	color: red;
	font-size: 20px;
`;

function App() {
	const [isClick, setClick] = useState(false);

	const clickAction = () => {
		setClick((prev) => !prev);
	};

	return (
		<>
			<ButtonStyle $isClick={isClick} onClick={clickAction}>
				ボタン
			</ButtonStyle>
			<TextStyle>{isClick ? 'クリックされました' : 'クリックされていません'}</TextStyle>
		</>
	);
}

export default App;

$isClick={isClick}でstateで管理しているisClickをButtonStyleに渡しています。
ButtonStyleでは、propsとして受け取れるため、props.$isClickでアクセスできます。
isClickの値の応じて、スタイルを動的に変えることができます。

◆styleの継承とオーバーライド

App.jsx
import { useState } from 'react';

import styled from 'styled-components';

const ButtonStyle = styled.button`
	display: inline-block;
	width: 100px;
	height: 50px;
	border: none;
	border-radius: 10px;
	margin: 5px;
	background-color: ${(props) => (props.$isClick ? 'skyblue' : 'green')};
`;

const TextStyle = styled.div`
	color: red;
	font-size: 20px;
`;

const SubButton = styled(ButtonStyle)`
	background-color: orange;
	&:hover {
		background-color: red;
	}
`;

function App() {
	const [isClick, setClick] = useState(false);

	const clickAction = () => {
		setClick((prev) => !prev);
	};

	return (
		<>
			<ButtonStyle $isClick={isClick} onClick={clickAction}>
				ボタン
			</ButtonStyle>
			<SubButton>サブボタン</SubButton>
			<TextStyle>{isClick ? 'クリックされました' : 'クリックされていません'}</TextStyle>
		</>
	);
}

export default App;

styled(ButtonStyle)とすることでButtonStyleのスタイルを継承できます。
また、その中でbackground-color: orange;と書くことでスタイルをオーバーライドできます。
さらに、&:hoverとすることで、hoverしたときのスタイルを当てることもできます。

スクリーンショット 2025-01-28 8.28.24.png

画像のようにボタンとサブボタンでクラス名が一意になるため、競合しないようになります。

■まとめ

デザインの衝突を避けるために、CSS-in-JSを使うのが1番にいいなと思いました

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?