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:

  1. 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.

  2. 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:

  1. 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.

  2. 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()

  1. Cleaner and more readable code

  2. Nested styles is achievable with this approach

  3. Separation of styles lead to smaller and readable files

#

References

🔗 Online Playground