React Native Component Library — Making things theme-able
— JavaScript, Software Development — 2 min read
As part of the React Native component library I’ve been building to make maintaining a legacy React Native app easier, I’ve been refactoring the way that styles were handled by the components.
The original complex components had a stylesheet to cover all the smaller components it contained, and when I broke these smaller components out into the component library I simply copied this stylesheet over, and removed any un-used styles by that component.
This approach led to a stylesheet for each component, and as the original stylesheet never re-used any of the styles it declared there was a lot of duplicated styles.
The team I’m working with expressed that the original styling implementation was a little awkward due to the number of different stylesheets, and they’d have preferred something similar to the CSS stylesheets they were used to working with.
With the need to refactor the implementation I decided a better approach would be to extract all the styles in the code base into one Object and that Object would use a hierarchy that followed the hierarchy in the component library.
With this approach a component located at src/inputs/pokemonPicker/Cell
that needed a style object called background
would use the inputs.pokemonPicker.Cell.background
property from the theme object.
This would then allow me to remove duplicate styles by extracting common values into other style definitions within the theme and updating the components to use those values.
Accessing the theme within the components
One of the main challenges of a moving all the components styles into one object is how to make that object accessible to the components without having to pass that prop to each component and then down to its children.
This is made really easy with the React Context Hook as it allows you to wrap your component in a Higher Order Component (HOC) that will grab the theme from the context and pass it to the wrapped component as a prop.
You then add the context providing component to the top of your component hierarchy and set the theme property that the theme HOC will pass to the component it’s wrapping.
Once you’ve set up the context you then need to add the theme property to the components propTypes
definition.
You can set the theme property as required or if you’d like to provide a default value (so you’re not dependent on passing the theme for things like testing) you can set the theme in defaultProps
.
One issue I encountered with the defaultProps approach however was in a HOC I had in the library for providing common theming around form inputs, it never received the default theme property so I had to instead set a default value for the theme when I de-structured the props object within the component.
The benefits of a theme
The biggest benefit of moving from stylesheets to a theme object has been that the code for each component is a lot cleaner now and as the theme object follows the same hierarchy as the component library it’s easier to see the logic behind what the styles represent.
The second is the ability to change the theme.
In order to show how easy having a theme allowed for broad stylistic changes to the app, I created an override object that would apply a ‘dark mode’ to the existing theme and used deepmerge
to create a new theme.
As the theme is just a JavaScript object this also opens up opportunities within the app to download different themes from a web server and present seasonal or event based style changes to the app.