LoginSignup
2
2

More than 3 years have passed since last update.

React: React Bootstrapでタブのカラーを変える ー useState()とuseEffect()を使って

Last updated at Posted at 2020-04-23

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>

とはいえ、コンポーネントの中身がブラックボックスになってしまって、手をつけにくいことも少なくありません。タブ(TabsTab)はその典型です。今回はこの作例のように、タブのカラーを動的に変えてみようとした試みのご紹介です([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ライブラリの読み込みが必要なことにもご注意ください。

src/App.js
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■タブが表示された

2004001_04.png

ブラックボックスでまず悩むのは、コンポーネントTabにクラスやスタイルを定めても、何の反応もないことです(Tabは抽象コンポーネントだからです)。ただ、ブラウザのデベロッパーツールで確かめると、タブの要素にはクラスnav-itemnav-linkが与えられていることはわかります。では、モジュールsrc/App.cssにスタイルを定めて確かめましょう。

src/App.css
.nav-item.nav-link {
    background-color: aqua;
}

タブのカラーは変わるものの、activedisabledについてはスタイルが上書きされてしまうようです(図002)。

図002■タブのカラーがCSSで変わった

2004001_01.png

動的にタブのスタイルを変える

今回は、タブのスタイル(カラー)を動的に変えたいと思います。ですから、src/App.cssの設定は一旦消してください。そして、モジュールsrc/App.jsにボタングループ(ButtonGroup)を以下のように加えます。ブラウザのデベロッパーツールでもうひとつわかることは、タブに「Tabsのid + '-tab-' + TabのeventKey」というidが定められていることです。

そこで、タブごとのカラー(tabColor)は配列(tabs)で定めて、ButtonGrouponClickイベントでタブのDOM要素にBootstrapのクラスを定めてみましょう(eventKeyはプロパティtitleに与えました)。

src/App.js
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■タブごとにカラーが設定される

2004001_02.png

ただし、カラーを与えたあとタブを切り替えると、それらのタブの色が戻ってしまいます(図004)。

図004■タブを切り替えると設定したカラーがもとに戻る

2004001_03.png

では、つぎのようにタブ(Tabs)をクリックしたとき、改めてカラーを設定し直せばよいのではないでしょうか。残念ながら、これは失敗します。カラーを再設定したあとに、タブ切り替えのスタイルで上書きされるようです。なお、カラーが設定されているか、リセットされたかをステートフックと呼ばれるuseState()関数でstate変数(mode)に定めました(「React Hooks: クラスのコンポーネントをuseState()で関数に書き替える」02「ステートフックuseState()を使う」参照)。

src/App.css
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)が収められます。

src/App.css
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の作例でお確かめださい。

2
2
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
2
2