Improved Conditional Styling in React Native

React.js logo

Since working with React Native, I’ve seen a lot of component renders using the following template to render conditional styles:

As it’s just a simple example, it might not be that noticeable, but you can guess it won’t scale very well when you’re rendering more than one component, and each one needs the use of conditional styles, or you just have more than one condition.

I wanted a less clustered render. Without thinking about implementation details, I tried to convert the previous example to something like:

And I also wanted styles.touchable to already have the specific styles of styles.touchableDisabled when props.disabled === true.

 

Implementation

A common approach to defining styles in React Native is defining a styles object in a file next to where the component is defined, and exporting it, like this:

The first step will be to define some typings. Precisely, we’ll determine the type of the styles object we want, [ComponentName]Styles.

That means, the difference to the type of the current style object is that our new type won’t have a buttonDisabled key. A [ComponentName]StylesContext type is also necessary, and it’ll have the information our style definition needs.

For this, we’ll create a new [ComponentName].types.ts file, where you may also declare the [ComponentName]Props type.

Next, we’ll modify our current style definition a little bit. Instead of making a styles object, we’ll make a getStyles function that receives a context: ButtonStylesContext and returns a ButtonStyles.

The trick for handling the disabled prop is to assign an array to the touchable key, instead of an object, and there make use of conditional styles.

It should look like this:

So far, we have already handled conditional styles from the styles declaration file. Yey!

Now we need to consume this getStyles function from the rendering side.

Doesn’t that render look beautifully tidy? Imagine what it can do to more extensive examples!

 

The useStyles hook

Our model is already working. However, we are now pretty much using inline-styles. We call the getStyles function on every render, and it returns a new object every time.

Let’s improve that!

The idea here is to make a createUseStyles function that given a getStyles function, it’ll return a hook that asks for the [ComponentName]StylesContext and returns a [ComponentName]Styles.

This will make the getStyles function to get called only when some context value changes, instead of in every render.

Making use of this new function, the Button.tsx file should now look like:

Yey again!

 

Potential benefits

This approach’s benefits go beyond component render readability, even though it was the initial purpose of this idea.

We have a createUseStyles function defined, and our getStyles functions only receive one parameter with a shape that depends on the component that uses it.

We could make every getStyles function receive a second parameter, an object with information about the whole application UI state that is the same for every component.

This object could have information about the current app theme, so every getStyles could use it. The Button render won’t need to be updated, as that info may come from the hook that createUseStyles returns.

The whole idea of using a hook to achieve this was to make it possible to add logic to the hook createUseStyles returns and give it the power to update the consumer component (Button, for instance) if needed (e.g., the theme of the app changes).

That’s not all.

Another common problem in React Native styling is the styling of components used inside the component currently rendered. See the next render:

When consuming a component like said Button, one would expect its props to provide a way to style both the container and touchable, probably looking like:

Leaving the render like this:

With our new styling approach, we could make use of our [ComponentName]Styles type and a modified useStyles to make it less cumbersome.

The useStyles hook will need to add a merging logic, where the styles it receives as its first parameter should overwrite the ones defined by the getStyles one.

This is how we’ll use the Button component:

And that’s it. Our button now rocks a simple API, with only one prop responsible for overriding sub-component styles!

 

Conclusions

Cleaner renders make for more readable code. No doubt.

The potential architecture benefits give us a little of web CSS in React Native. Think about it: We already had this additional [ComponentName].styles.ts file that handles styles.

On web, we have subclasses.

In this approach, we have a context object.

The interesting part, though, is that we could now override styling of sub-components of components we’re rendering from inside our [ComponentName].styles.ts file, just like you’d do with web CSS, pointing to children that have a specific class.

And because it’s TypeScript, it’s typed!

In case you need a hand with styling in React Native, or developing a React Native mobile app, drop us a line! We’d love to help 🙂

 

Let’s talk!