DEV Community

Stefano Magni
Stefano Magni

Posted on • Edited on

RouteManager UI coding patterns: React

This is a non-exhaustive list of the coding patterns the WorkWave RouteManager's front-end team follows. The patterns are based on years of experience writing, debugging, and refactoring front-end applications with React and TypeScript but evolves constantly. Most of the possible improvements and the code smells are detected during the code reviews and the pair programming sessions.

(please note: I do not work for WorkWave anymore, these patterns will not be updated)

(last update: 2022, March)

Prefer duplication over abstraction when dealing with low complexity and boilerplate

Don't get code "smarter" just for the sake of avoiding a bit of duplication. Often, duplication improves readability and gets code ready for future changes.

// ❌ don't
const filterValues = {
  all: 'all',
  winners: 'winner',
  losers: 'loser',
}

function FilterBar(props) {
  const isActive = (filter) => filter === props.currentFilter;
  const filters = Object.values(filterValues);

  return filters.map(filter =>
    <div className={isActive(filter) ? 'active' : ''}>{filter}</div>
  )
}

// ✅ do
function FilterBar(props) {
  const { currentFilter } = props

  return <>
    <div className={currentFilter === 'all' ? 'active' : ''}>all</div>
    <div className={currentFilter === 'winner' ? 'active' : ''}>winner</div>
    <div className={currentFilter === 'loser' ? 'active' : ''}>loser</div>
  </>
}
Enter fullscreen mode Exit fullscreen mode

Avoid "return null" components

The component's parent decides if the component is rendered or not.

// ❌ don't
function Poll() {
  const [pollEnabled, setPollEnabled] = useState(false)

  return <>
    <TogglePoll setPollEnabled={setPollEnabled} />
    <PollConfirmation pollEnabled={pollEnabled} />
  </>
}

function PollConfirmation(props) {
  if(!props.pollEnabled) return null

  return <>Poll is enabled</>
}

// ✅ do
function Poll() {
  const [pollEnabled, setPollEnabled] = useState(false)

  return <>
    <TogglePoll setPollEnabled={setPollEnabled} />
    {pollEnabled && <PollConfirmation  />}
  </>
}

function PollConfirmation() {
  return <>Poll is enabled</>
}
Enter fullscreen mode Exit fullscreen mode

Move long conditions out of the JSX

Components' JSX can get unreadable in a while, always move out long conditions.

// ❌ don't
function Foo() {
  /* ... rest of the code... */

  return <>
    {(cond1 && status === 'bar' || date === today) && <Bar />}
    {(cond2 && cond3 date !== today) && <Baz />}
  </>
}

// ✅ do
function Foo() {
  /* ... rest of the code... */

  const renderBar = cond1 && status === 'bar' || date === today
  const renderBaz = cond2 && cond3 date !== today

  return <>
    {renderBar && <Bar />}
    {renderBaz && <Baz />}
  </>
}
Enter fullscreen mode Exit fullscreen mode

Get JSX combinations/states clear

JSX with a lot ternaries and logic conditions can make simple cases hard to read. The reader will spend a significant amount of mental energies to detect what is rendered and what isn't.

// ❌ don't
function Theme(props) {
  const { theme } = props

  return <>
    {theme === 'dark' ? <div>
      <BlackBackgroundButton />
      <GreyBackgroundButton />
    </div> : <div>
      <WhiteBackgroundButton />
      <SepiaBackgroundButton />
    </div>}

    <ColorsPalette theme={theme} />

    {theme === 'dark' ? <DarkThemeSample /> : <LightThemeSample />}

    {theme === 'light' && <LightThemesList />}
  </>
}

// ✅ do
function Theme(props) {
  const { theme } = props

  switch(theme) {
    case 'dark':
      return <>
        <div>
          <BlackBackgroundButton />
          <GreyBackgroundButton />
        </div>

        <ColorsPalette theme='dark' />
        <DarkThemeSample />
      </>

    case 'light':
      return <>
        <div>
          <WhiteBackgroundButton />
          <SepiaBackgroundButton />
        </div>

        <ColorsPalette theme='light' />
        <LightThemeSample />
        <LightThemesList />
      </>
  }
}
Enter fullscreen mode Exit fullscreen mode

The next step is returning a new, dedicated component, for every switch case.

// ❌ don't
function Theme(props) {
  const { theme } = props

  switch(theme) {
    case 'dark':
      return <>
        <div>
          <BlackBackgroundButton />
          <GreyBackgroundButton />
        </div>

        <ColorsPalette theme='dark' />
        <DarkThemeSample />
      </>

    case 'light':
      return <>
        <div>
          <WhiteBackgroundButton />
          <SepiaBackgroundButton />
        </div>

        <ColorsPalette theme='light' />
        <LightThemeSample />
        <LightThemesList />
      </>
  }
}

// ✅ do
function Theme(props) {
  switch(props.theme) {
    case 'dark':
      return <DarkTheme />

    case 'light':
      return <LightTheme />
  }
}
Enter fullscreen mode Exit fullscreen mode

Prefer CSS Variables to optimize Material UI styles

Material UI injects a lot of styles into the DOM. CSS variables limit the amount of injected styles for dynamic properties.

// ❌ don't
const useStyles = makeStyles({
  foo: props => ({
    backgroundColor: props.backgroundColor,
  }),
});

function MyComponent(props) {
  const classes = useStyles(props);

  return <div className={classes.foo} />
}

// ✅ do
const useClasses = makeStyles({
  foo: { backgroundColor: 'var(--background-color)' }
})

function useStyles(backgroundColor: string) {
  const classes = useClasses()

  const styles = useMemo(() => {
    return {
      foo: { '--background-color': `${backgroundColor}` } as CSSProperties,
    }
  }, [backgroundColor])

  return [classes, styles] as const
}

function MyComponent(props) {
  const [classes, styles] = useStyles(props.backgroundColor)

  return <div className={classes.foo} styles={styles.foo} />
}

Enter fullscreen mode Exit fullscreen mode

Create component's explicit variants

Component explicit variants are more predictable and easy to refactor.

// ❌ don't
function Consumer() {
  return (
    <>
      <Button>Click me</Button>
      <Button src="./image.jog" />
    </>
  )
}

// ✅ do
function Consumer() {
  return (
    <>
      <Button variant="text">Click me</Button>
      <Button variant="image" src="./image.jog" />
    </>
  )
}
Enter fullscreen mode Exit fullscreen mode

Avoid naming props with CSS property-like names

Props named like CSS properties create ambiguity, the reader cannot understand if the prop is used to directly set the CSS property or not.

// ❌ don't
function Consumer() {
  return (
    <Stack align="left" /> // is "align" used to set CSS Flex' align?
  )
}

// ✅ do
function Consumer() {
  return (
    <Stack variant="left" /> // variant does not sound like a CSS property, nice!
  )
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)