You can switch a class names of React components with deciders, and it feels like a breeze.
Imagine you have a Header component. Its class name is always header
, but on a mobile device, it should be also mobile
. Oh, and if the width
prop is less than ”400px’, it should be also narrow
. Oh, and it should be hidden
when either hidden
prop is truthy but not equals to "false"
string, and it should be also fixed
when it isn’t either narrow
or mobile
.
Wow. Such a mess.
But wait a minute. What if I tell you that you can tackle this problem with just five lines of pretty readable code?
className={ decide(styles, {
header: true,
mobile: props.isMobile,
narrow: parseInt(props.width) < 400,
hidden: (props.hidden !== "false" && Boolean(props.hidden)),
fixed: parseInt(props.width) >= 400 || !props.isMobile
})}
Introducing Deciders
Decider is something that makes decisions.
I was fed up making it manually, so I’ve created a library to tackle that mess. How? After all, we either apply the class name or not. So I thought, what if we can declare class names as the keys and the conditions should be values?
{
header: true, // will be always applied
mobile: props.isMobile // will be applied according to a prop
}
The result class names should be either "header"
or "header mobile"
.
The thing above is just an object. As long as it’s not some kind of magical class instance, you can even generate it. You can return it from a function or promise. You can bring this from your REST API. This is simple and declarative.
Let’s call this kind of object a decision matrix.
Now, we need to get this to work.
import decide from 'decider'
import styles from './header.module.css'
const classNames = decide(styles, {
header: true,
mobile: props.isMobile
})
Here you go. It’s meaningful. It’s simple and understandable. You can export it. The HTML and CSS are declarative, so as deciders.
Let’s apply the styles:
export default props => (
<header className={ classNames }>
...
</header>
)
But what about order? Well, the class names order doesn’t matter in HTML, so it doesn’t matter in deciders either.
Let’s solve the header class names’ problem I declared at the beginning of the article! Here we go:
import decide from 'decider'
import styles from './header.module.css'
export default props => (
<header className={ decide(styles, {
header: true,
mobile: props.isMobile,
narrow: parseInt(props.width) < 400,
hidden: props.hidden !== "false" || props.hidden,
fixed: parseInt(props.width) >= 400 || !props.isMobile
})} >
...
</header>
)
Five strings of purely declared decisions.
Differences from classnames
Decide
is a pure function. It works without that bind magic, so you can use it virtually anywhere.- Smaller bundle size: Decider is just 171 bytes away!
- Syntax and usage are much easier to adopt
Wrapping up
- Switching class names manually is error-prone and overall not a pleasant thing to do
- You can switch class names easily with deciders
- If you want to do this, you should declare a decision matrix – an object which has class names as keys and booleans as values
- Decider is a
decide(styles, decisionMatrix)
. It returns a class names’ string - You pass this string as a
className
prop to your component, and it works
I want to adopt this right now!
Alright, that’s it! Keep your code clean, meaningful and understandable!