React applications, with all their DOM manipulation and re-rendering, give us a pretty good environment to do things like add and remove class names to apply style changes to our app. In this post we'll take a look at two methods of doing just that. In the first method we'll simply be doing everything ourselves, and in the second we'll be making use of the officially supported Classnames utility. This isn't exhaustive by any means, but it is a good example of how our application state and styles can interact.


The Do-Everything-Ourselves Method

We're going to make an awesome square that turns into a circle when we click on it, then back to a square if we click again. Internet magic at its finest. Let's write some simple css rules for our square, and a general transition rule for the animation.

.square {
  width: 200px;
  height: 200px;
  border-radius: 0%;
  background-color: blue;
  transition: all 0.5s ease-out;
}

Cool. Now let's build the React component for our square.

import React from 'react';

class CoolSquare extends React.Component {
    constructor(props){
        super(props);

        this.state = {
            isClicked: false
        };
    }

    render() {

        var classLabel = "square";
        if(this.state.isClicked == true){
            classLabel += " active";
        }

        return (
        <div className={classLabel} 
          onClick={() => this.itWasClicked}>
        </div>
        );
    }

    itWasClicked(){
        this.setState({isClicked: !this.state.isClicked});
    }

}

export default CoolSquare;

So here we've created a CoolSquare component and given it a property isClicked that we have initialized to be false. Next we're creating a classLabel variable for the square that will conditionally handle our class names through an if statement: if isClicked is false we have the default 'square', and if it's true we have 'square active'. Next we return our div, with the corresponding classLabel giving the element its class. Lastly we have the addition of an event handler to the element that will trigger a state change when it has been clicked, flipping the isClicked property between true and false. This is the really useful bit.

Thanks to the way React handles state, any change in the state of our component (like that click event) will trigger a re-render of our app, giving us the classLabel for the current state of the square and in turn the appropriate class name for its element. So all we have to do now if we want to make that square a circle when we click on it, is add a border-radius rule for 'square active'.

.square.active {
  border-radius: 50%;
}

And voila, we've got our dynamic classes doing their thing.


The Classnames Utility Method

So while in our simple example doing this ourselves isn't that painful, real-world applications are typically going to have more than one element, more than one potential state and added conditional logic. The suggested way to work with this extra complexity is a javascript package called Classnames. It gives you the use of a classNames function that will take any arbitrary number of string or object arguments, and output only those which are truthy. So rather than writing if statements for each class condition we want to check, we can represent them directly as an object. 

To borrow an example from the Classnames documentation, let's say we are building a button component, and we want this button to be as versatile as possible because we're using it all across our app. We may have states for if the button is clicked, if it's hovered, an un-clickable state while our app processes something, an error state, a flashing strobe-light state and so on. Again, rather than writing repeated if statements, Classnames allows us to do the following:

import React from 'react';
import classNames from 'classnames';

class CoolButton extends React.Component {
  
  // ...
  
  render () {
    var classLabel = classNames({
      'btn': true,
      'btn-clicked': this.state.isClicked,
      'btn-hover': this.state.isHovered,
      'btn-process': this.state.isProcessing,
      'btn-strobe-lights': this.state.isStrobeLightTime
    });
    
    return (
      <button className={classLabel}>Click Me!</button>
    );
  }
}

export default CoolButton;

Now our classNames function will evaluate and return only those properties whose current state is true, allowing us to easily build our classLabel. This gives us the flexibility to handle much more complicated cases (you can add whatever logic you'd like to evaluate a class name), while still expressing them simply. 

We can actually take this whole styling components through state one step further with something like Radium, but I'll save that for another time. 

 

-Brad