Conditional styling in React Native
PUBLISHED ON: Tuesday, Aug 9, 2022
This is an example of conditional styling, in general:
<Container
data={data}
containerStyles={[
styles.contentContainer,
props.renderHeader && styles.contentContainerWithHeader,
]}
{...props}
/>
It might not be that noticeable, but there are a few issues with this approach:
-
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.
-
Code can be messier over time.
#
The createUseStyles() helper
To tackle these issues, we create a helper function named createUseStyles
. This function does the following:
-
It takes another callback function as an argument and returns a hook that asks for style context defined by the callback function and returns the styles accordingly.
-
Instead of execution on every render, the callback function will get executed only when some of the context values changes.
Here is how the implementation looks like:
import React from "react";
export const createUseStyles = <TContext, TStyles>(
getStyles: (context: TContext) => TStyles
): ((context: TContext) => TStyles) => {
return (context) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
return React.useMemo(() => getStyles(context), Object.values(context));
};
};
#
Using createUseStyles()
Let's say we want to apply different styles to a circle based on whether a button is pressed or not.
One approach as we discussed above is to define two style properties and use inline conditionally.
The other approach is to use createUseStyles
to create a hook that returns the styles. Let's check it out below. We create a separate styles.ts
file for separation of concern which contains the following:
import { StyleProp, TextStyle, ViewStyle } from "react-native";
import { createUseStyles } from "./createUseStyles";
// Define style context properties
type StyleContext = {
pressed?: boolean;
};
// Define type of styles object
type StyleType = {
container: StyleProp<ViewStyle>;
button: {
container: StyleProp<ViewStyle | TextStyle>;
text: StyleProp<TextStyle>;
};
};
// Function to get styles based on the context
const getStyles = ({ pressed = false }: StyleContext): StyleType => ({
container: {
backgroundColor: pressed ? "#813a8c" : "#f194ff",
...,
},
button: {
container: {
backgroundColor: !pressed ? "#813a8c" : "#f194ff",
...,
},
text: { ... },
}
});
// Returns a hook that when called will execute a
// memoized version of `getStyles` and returns the styles
export const useStyles = createUseStyles(getStyles);
Finally, inside the component, we get styles as follows:
import React, { useState } from "react";
import { Pressable, View } from "react-native";
import { useStyles } from "./styles";
const App = () => {
const [pressed, setPressed] = useState(false);
const styles = useStyles({ pressed });
return (
<>
<View style={styles.container} />
<Pressable
style={styles.button.container}
onPress={() => setPressed(!pressed)}
>
<Text style={styles.button.text}>PRESS ME</Text>
</Pressable>
</>
);
};
#
Benefits of createUseStyles()
-
Cleaner and more readable code
-
Nested styles is achievable with this approach
-
Separation of styles lead to smaller and readable files
#
References