Reactで汎用性のあるbuttonコンポーネントの作成 with styled-components
ボタンはwebページ上に必ずと言っていいほど設置されているパーツです。
似たような見た目のボタンでもクリックしたときの処理は違いますし、スタイリングも様々です。
そんないろんな見た目、役割を担うボタンコンポーネントを作成しましょう。
今回は2つのパターンを用意しました。
- コンポーネントへ渡す
props
を利用して実装 -
styled-components
の機能を使用して実装
こんなやつを作っていきます。
この記事での「汎用性」の定義
- ボタンクリック時の関数を割り振ることができる
- ボタンのスタイルの使い分けを宣言的に行える
- ボタンの子要素も自由に設定できる
props
を利用して実装
1.コンポーネントへ渡すButton
コンポーネントを宣言するときに、渡されたprops
によって予めスタイルが実装してあるボタンコンポーネントを返す、というものです。
onClick
やchild
などのprops
名でイベントハンドラーやボタンのテキストも渡してあげます。
ここで作成したButton
コンポーネントを宣言するときのイメージはこんな感じです。
<Button style="danger" onClick={handleOnClick} child={赤いボタンです} />
上記の宣言だとこんな感じのボタンが出来上がります。
① BaseButtonコンポーネントを作成
import React from "react";
import styled from "styled-components";
const BaseButton = styled.button`
text-align: center;
color: white;
width: 100%;
min-width: 100px;
`;
こちらのBaseButton
コンポーネントを継承していろんなスタイルのボタンを作成していきます。
② 実際に使いたいスタイルのボタンを作成
今回は緑色のボタンButtonPrimary
と赤色のボタンButtonDanger
を作成します。
styled-components
で用意されているスタイルの拡張を使用して、BaseButton
のスタイルを拡張します。
import React from "react";
import styled from "styled-components";
const BaseButton = styled.button`
text-align: center;
color: white;
width: 100%;
min-width: 100px;
`;
// 緑色のボタン(BaseButtonを基に拡張)
const ButtonPrimary = styled(BaseButton)`
background: green;
`;
// 赤色のボタン(BaseButtonを基に拡張)
const ButtonDanger = styled(BaseButton)`
background: red;
`;
Button
コンポーネントに渡ってきたprops
によって作成するボタンのスタイルを決定するロジックを作成
③ いろんな方法があるかと思いますが、今回はボタンの一覧を表すオブジェクトを作成して実装します。
import React from "react";
import styled from "styled-components";
const BaseButton = styled.button`
text-align: center;
color: white;
width: 100%;
min-width: 100px;
`;
// 緑色のボタン(BaseButtonを基に拡張)
const ButtonPrimary = styled(BaseButton)`
background: green;
`;
// 赤色のボタン(BaseButtonを基に拡張)
const ButtonDanger = styled(BaseButton)`
background: red;
`;
// 全ボタンコンポーネント
const buttonStyleLists = {
default: BaseButton,
primary: ButtonPrimary,
danger: ButtonDanger,
};
// propsのstyleTypeでボタンのスタイルを分岐
const Button = ({ styleType, onClick, child }) => {
const Component = buttonStyleLists[styleType] || buttonStyleLists.default;
// Component変数に格納したコンポーネントでReact要素を作成
return <Component onClick={onClick}>{child}</Component>;
};
export default Button;
所感
正直、変数をJSXのタグに使うことができるのは知りませんでした。
ただこれはJSX記法がReact.createElementのシンタックスシュガーということを考えれば当然のことですね。
⚠️変数を大文字にしないとReact要素は生成されないので注意!
上記のコードの例だと、変数名がComponent
だと上手くいきますが、component
だとボタンは描画されません。
JSXを深く理解する - React
styled-components
の機能を使用して実装
2. こちらの方法はstyled-components
の機能に依存します。
公式ドキュメントに記載があります。
※拙い和約失礼します。
The styled method works perfectly on all of your own or any third-party component, as long as they attach the passed className prop to a DOM element.
styledメソッドはあなたが作成したコンポーネントや他のどのコンポーネントでも、DOMエレメントに className が渡されている限り動作します。
① BaseButtonコンポーネントを作成
import styled from "styled-components";
const BaseButton = styled.button`
text-align: center;
color: white;
width: 100%;
min-width: 100px;
`;
これは前回の1.コンポーネントへ渡す props を利用して実装
と同じです。
このコンポーネントを基に他のボタンのスタイルを行っていきます。
② Buttonコンポーネントを完成させる
<BaseButton>
コンポーネントとしてexportしてしまいます。
この際に気をつけるべきところは、className
を渡してあげることです。
className
を渡してあげないと、先ほど説明したsytled-components
でのコンポーネントの追加スタイリングができなくなってしまいます。
import React from "react";
import styled from "styled-components";
const BaseButton = styled.button`
text-align: center;
color: white;
width: 100%;
min-width: 100px;
`;
// classNameを記載する
export const Button = ({ children, className, onClick }) => {
return <BaseButton className={className} onClick={onClick} >{children}</Button>;
};
③ Buttonコンポーネントを宣言する時にコンポーネントのスタイルを拡張する
試しにApp.js
で実装してみます。
呼び出す時にonClick
とchildren
も渡してあげましょう。
import logo from './logo.svg';
import './App.css';
import Button from './Button'; // 先ほどのButtonコンポーネントをimport
import styled from 'styled-components';
// Buttonコンポーネントのスタイルを拡張して新しいコンポーネントを作成
const DangerButton = styled(Button)`
background: red;
`;
function App() {
return (
<div className='App'>
<header className='App-header'>
<DangerButton onClick={() => console.log("Hello")} children={"text"}></DangerButton>
</header>
</div>
);
}
export default App;
所感
ボタンのコンポーネントを宣言するたびにスタイルを設定しないといけないので、プロジェクトの規模が大きくなってきたら管理が大変になりそうですね。。。
まとめ
今回は2つの方法でボタンコンポーネントを作りました。
間違いなどがあったら是非コメントで教えてください。
Discussion
ありがとうございます。参考になりました。
特にJSXの変数名のあたり完全に忘れたまま使っていました笑
文脈でわかるとは思いますが以下タイポ指摘です(このコード例しか見れてません)。
diff記法、初めて使いました。便利ですね。
少しでも参考になったのであれば嬉しいです😊
ご指摘ありがとうございます!
修正しました!