![React.js logo](https://orangeloops.com/wp-content/uploads/2019/07/1_y6C4nSvy2Woe0m7bWEn4BA.png)
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 🙂