← All Articles A Product of Kinsa Creative

Set a class name in React based on a button value

In this example, tabs, comprised of an unordered list of button elements, control what content is shown (defined elsewhere). This uses the value of the <button> element within each tab to determine which tab was clicked by tying an event handler to the click. It then calls the function to change the content the tab is controlling.

For the sake of brevity it omits aria-controls attributes and any other attributes that don't help directly illustrate the point. See w3.org for a table of relevant attributes that would likely be applied to this tabbed navigation.

import { useState } from 'react';
import PropTypes from 'prop-types';

function ListItem({ item, value, clickEventHandler, activeButton }) {

    const activeClassName = 'active';

    return(
            <li className={`nav-tab ${activeButton === item ? activeClassName : ''}`}>
                <button className={`${activeButton === item ? activeClassName : ''}`} name="nav-tabs" value={item} onClick={clickEventHandler}>{value}</button>
            </li>
    )
}

function NavTabs({ changeEventHandler }) {
    // useState() to store which button is active
    const [activeButton, setActiveButton] = useState(null);

    // modify state for the active button before passing on to the onChange event handler
    const handleButtonClick = (event) => {
        // store the click event's target's value
        setActiveButton(event.target.value);
        // modify the content shown based on the tab clicked (calls a function that has been passed in from elsewhere)
        changeEventHandler(event);
    };

    // mock up options that should really be queried from somewhere
    // the key, e.g. 'item-1', is what is set as the `value` on the button
    // it is also what is checked against when setting the active state for the button and the button's containing list element.
    const items = [
        { 'item-1': 'Item 1' },
        { 'item-2': 'Item 2' }
    ];

    return(
            <ul>
                {items.map((itemObj, index) => {
                    const item = Object.keys(itemObj)[0];
                    const value = itemObj[item];
                    return (
                            <ListItem
                                    key={index}
                                    item={item}
                                    value={value}
                                    clickEventHandler={handleButtonClick}
                                    activeButton={activeButton}
                            />
                    );
                })}
            </ul>
    );
}

// Define prop types
ListItem.propTypes = {
    item: PropTypes.string,
    value: PropTypes.string,
    clickEventHandler: PropTypes.func,
    activeButton: PropTypes.object,
}

NavTabs.propTypes = {
    onChange: PropTypes.func,
}

Clearing the active state by clicking off or pressing escape

Since neither tab is selected by default, if you click off, or press escape, you might expect the tabs to return to their default state. To do this, import useEffect from React along with useState, then in the NavTabs component, add below the definition of activeButton and setActiveButton:

const handleWindowClick = (event) => {
    // Check if the click is outside the React component
    if (
            !event.target.closest('li')
            && !event.target.closest('button')
    ) {
        // set neither button to active
        setActiveButton(null);
        // modify the content shown based on the tab clicked (calls a function that has been passed in from elsewhere)
        changeEventHandler(event);
    }
};

// Event handler for window keydown
const handleWindowKeydown = (event) => {
    // Check if the key pressed was the escape key
    if (event.key === 'Escape') {
        // set neither button to active
        setActiveButton(null);
        // modify the content shown based on the tab clicked (calls a function that has been passed in from elsewhere)
        changeEventHandler(event);
    }
};

// Add event listener to window object when component mounts
useEffect(() => {
    window.addEventListener('click', handleWindowClick);
    window.addEventListener('keydown', handleWindowKeydown);

    // Remove event listener when component unmounts
    return () => {
        window.removeEventListener('click', handleWindowClick);
        window.removeEventListener('keydown', handleWindowKeydown);
    };
}, []);

Demo

Open the code inspector to see the active class applied to each button and its parent list item when clicked.

See the Pen Untitled by Kinsa Creative (@kinsaux) on CodePen.

Feedback?

Email us at enquiries@kinsa.cc.