Jynx logoJynx

Compose

Improve performance when using multiple style functions
import { compose } from 'jynx'

The compose utility can help improve performance when using several style functions within a component by combining them into a single function that accepts the collective arguments of the functions it's been passed.

Usage

To use the compose utility, include it within your components style argument, and pass it a list of style functions.

If you're using typescript, define an intersection of the selected style functions prop types, then pass this as a generic argument to both the component and the utility.

/* Import the `compose` utility along with any required functions */
/* and their corresponding props types */
import { compose, color, ColorProps, space, SpaceProps, grid, GridProps } from 'jynx'


/* Define an intersection of all the necessary prop types */
/* (or pass them directly to the component) */
type ComboProps = ColorProps & SpaceProps & GridProps


/* Then create a component using the type and include `compose` within */
/* its style argument, passing to it a list of style functions */
export const Component = styled.div<ComboProps>`
  ${compose<ComboProps>(color, space, grid)}
`


/* Or using the legacy functional syntax */
export const Component = styled.div<ComboProps>(props => `
    ${compose<ComboProps>(color, space, grid)}
  `)

How it works

The compose utility works by merging a list of style functions into a single function that accepts the collective arguments of the functions it's been passed.

This benefits performance by:

  1. allowing the styles to be parsed concurrently rather than in series
  2. returning a single style object rather than one for each function

To help visualize this, consider the following:

/* We create a Container and include multiple style functions */
/* within its style argument */
export const Container = styled.div<ColorProps & SpaceProps>`
  ${color}
  ${space}
`

/* Then use it in a React component and pass it some */
/* responsive props */
const Layout: React.FC = ({ children }) => (
  <Container color={['red', 'green', null, 'blue']} padding={[16, null, 20, 24]}>
    {children}
  </Container>
)

Here, the props passed to <Container> are consumed, parsed and returned by their respective style functions, resulting in the following being rendered in the components style argument:

export const Container = styled.div<ColorProps & SpaceProps>`
  ${{
    color: 'red',
    '@media (min-width: 640px)': {
      color: 'green'
    },
    '@media (min-width: 1280px)': {
      color: 'blue'
    }
  }}

  ${{
    padding: '16px',
    '@media (min-width: 960px)': {
      padding: '20px'
    },
    '@media (min-width: 1280px)': {
      padding: '24px'
    }
  }}
`

However, when constructing the same code using the compose utility, we see the styles being parsed concurrently and returned as a single object, meaning the resulting style argument would look something like this:

export const Container = styled.div<ColorProps & SpaceProps>`
  ${{
    color: 'red',
    padding: '16px',
    '@media (min-width: 640px)': {
      color: 'green'
    },
    '@media (min-width: 960px)': {
    padding: '20px'
    },
    '@media (min-width: 1280px)': {
    color: 'blue'
    padding: '24px'
    }
  }}
`

Types

The compose utility is defined as simple function that takes a deconstructed array of style functions as arguments and returns a single style function.

A generic parameter T is used to define the props that can be passed to both the argument functions and the returned style function.

const compose: <T extends Record<string, any>>(...styleFunctions: StyleFunction<{ [K in keyof T]: T[K] }>[]) => StyleFunction<T>