React Bootstrapは、Bootstrapのスタイルが適用されたコンポーネントをReactアプリケーションに組み込めるライブラリです。この3月24日に正規バージョンv1.0.0がリリースされました。Bootstrapのみでは<div>
などにやたらとクラスを詰め込んだDOM要素があふれます。それに対して、Reactのコンポーネントですっきりと組み立てられるのが利点です。
Bootstrap「Jumbotron」のコード例
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
<div class="dropdown-menu" aria-labelledby="dropdown01">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
</ul>
React Bootstrapで再現したコード例
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="#home" active>
Home
</Nav.Link>
<Nav.Link href="#features">Link</Nav.Link>
<Nav.Link href="#pricing" disabled>
Disabled
</Nav.Link>
<NavDropdown title="Dropdown" id="basic-nav-dropdown">
<NavDropdown.Item href="#action">Action</NavDropdown.Item>
<NavDropdown.Item href="#another">
Another action
</NavDropdown.Item>
<NavDropdown.Item href="#something">Something</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item href="#separated">
Separated link
</NavDropdown.Item>
</NavDropdown>
</Nav>
とはいえ、コンポーネントの中身がブラックボックスになってしまって、手をつけにくいことも少なくありません。タブ(Tabs
やTab
)はその典型です。今回はこの作例のように、タブのカラーを動的に変えてみようとした試みのご紹介です([set]と[reset]ボタンでタブのカラーが設定・解除できます)。
CSSクラスでスタイルを定める
Reactアプリケーションは、create-react-app
でひな形をつくることにします。ひな形Reactアプリケーションのつくり方は「Create React App 入門 01: 3×3のマス目をつくる」01「Reactアプリケーションのひな形をつくる」をお読みください。
さらに、React BootstrapとBootstrapをインストールします(「Introduction」参照)。
npm install react-bootstrap bootstrap
React BootstrapサイトのTabbed componentsを参考に、モジュールsrc/App.js
はつぎのように書き替えます。これで、まずはタブが表示されるはずです(図001)。BootstrapのCSSライブラリの読み込みが必要なことにもご注意ください。
import React from 'react';
import {
Tabs,
Tab,
ButtonGroup,
ToggleButton,
Container,
} from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
function App() {
const tabsId = 'uncontrolled-tab-example';
return (
<Tabs defaultActiveKey="home" id={tabsId}>
<Tab eventKey="home" title="Home">
Home
</Tab>
<Tab eventKey="profile" title="Profile">
Profile
</Tab>
<Tab eventKey="contact" title="Contact" disabled>
Contact
</Tab>
</Tabs>
);
}
export default App;
図001■タブが表示された
ブラックボックスでまず悩むのは、コンポーネントTab
にクラスやスタイルを定めても、何の反応もないことです(Tab
は抽象コンポーネントだからです)。ただ、ブラウザのデベロッパーツールで確かめると、タブの要素にはクラスnav-item
とnav-link
が与えられていることはわかります。では、モジュールsrc/App.css
にスタイルを定めて確かめましょう。
.nav-item.nav-link {
background-color: aqua;
}
タブのカラーは変わるものの、active
とdisabled
についてはスタイルが上書きされてしまうようです(図002)。
図002■タブのカラーがCSSで変わった
動的にタブのスタイルを変える
今回は、タブのスタイル(カラー)を動的に変えたいと思います。ですから、src/App.css
の設定は一旦消してください。そして、モジュールsrc/App.js
にボタングループ(ButtonGroup
)を以下のように加えます。ブラウザのデベロッパーツールでもうひとつわかることは、タブに「Tabsのid + '-tab-' + TabのeventKey
」というid
が定められていることです。
そこで、タブごとのカラー(tabColor
)は配列(tabs
)で定めて、ButtonGroup
のonClick
イベントでタブのDOM要素にBootstrapのクラスを定めてみましょう(eventKey
はプロパティtitle
に与えました)。
function App() {
const tabs = [
{title: 'home', tabColor: ['text-white', 'bg-primary']},
{title: 'profile', tabColor: ['text-white', 'bg-success']},
{title: 'contact', tabColor: ['bg-warning']},
];
const tabsId = 'uncontrolled-tab-example';
const setColor = (event) => {
const value = event.target.value;
if (value) {
console.log('value:', value);
tabs.forEach((tab) => {
const tabElement = document.getElementById(tabsId + '-tab-' + tab.title);
switch (value) {
case '2':
tabElement.classList.add(...tab.tabColor);
break;
case '1':
tabElement.classList.remove(...tab.tabColor);
break;
default:
break;
}
});
}
};
return (
<Tabs defaultActiveKey="home" id={tabsId}>
<Tab eventKey="home" title="Home">
<Container className="pt-2">
<ButtonGroup toggle onClick={setColor}>
<ToggleButton type="radio" name="radio" defaultChecked value="1">
reset
</ToggleButton>
<ToggleButton type="radio" name="radio" value="2">
set
</ToggleButton>
</ButtonGroup>
</Container>
</Tab>
</Tabs>
);
}
これで、設定ボタン[set]をクリックすると、タブごとにそれぞれのカラーが加えられ、解除の[reset]ボタンでもとに戻ります(図003)。
図003■タブごとにカラーが設定される
ただし、カラーを与えたあとタブを切り替えると、それらのタブの色が戻ってしまいます(図004)。
図004■タブを切り替えると設定したカラーがもとに戻る
では、つぎのようにタブ(Tabs
)をクリックしたとき、改めてカラーを設定し直せばよいのではないでしょうか。残念ながら、これは失敗します。カラーを再設定したあとに、タブ切り替えのスタイルで上書きされるようです。なお、カラーが設定されているか、リセットされたかをステートフックと呼ばれるuseState()
関数でstate
変数(mode
)に定めました(「React Hooks: クラスのコンポーネントをuseState()で関数に書き替える」02「ステートフックuseState()を使う」参照)。
import React, { useState } from 'react';
function App() {
const [mode, setMode] = useState('1');
const setColor = (event) => {
if (value) {
setMode(value);
setTabsColor(value);
}
};
const setTabsColor = (value) => {
tabs.forEach((tab) => {
const tabElement = document.getElementById(tabsId + '-tab-' + tab.title);
switch (value) {
case '2':
tabElement.classList.add(...tab.tabColor);
break;
case '1':
tabElement.classList.remove(...tab.tabColor);
break;
default:
break;
}
});
};
return (
<Tabs defaultActiveKey="home" id={tabsId} onClick={() => setTabsColor(mode)}>
</Tabs>
);
}
Reactコンポーネントツリーがレンダーされたあとに、カラーは再設定しなければなりません。そのために使えるのが、副作用フックのuseEffect()
関数です。
useEffect
に渡された関数はレンダーの結果が画面に反映された後に動作します。
(「useEffect」より)
useEffect()
の第1引数は、行うべき処理をコールバックとして定めた副作用関数です。そして、第2引数は依存配列と呼ばれ、その変化により副作用関数を発動する変数(changed
)が収められます。
import React, { useState, useEffect } from 'react';
function App() {
const [changed, setChanged] = useState(false);
useEffect(() => {
setTabsColor(mode);
setChanged(false);
}, [changed]);
return (
<Tabs defaultActiveKey="home" id={tabsId} onClick={() => setChanged(true)}>
</Tabs>
);
}
これでカラーを加えたあとタブの切り替えによりスタイルが変わっても、副作用関数がレンダー後に設定し直します。動きは、冒頭にもご紹介したCodeSandboxの作例でお確かめださい。