Skip to content

React.js

Conditional rendering with more than 2 options

Here is an alternative to using ternary expressions (and especially nested ternary expressions): use an IIFE with conditionals or switch statements.

jsx
function MyComponent({ someValue }) {
    return (
        <WrappingComponent>
        {(() => {
            if (someValue === 'option1') {
                return <div>Option 1</div>
            }
            if (someValue === 'option2') {
                return <div>Option 2</div>
            }
            return <div>Default</div>
        })()}
        </WrappingCompnent>
    )
}

How to handle unstable prop values

Problem: you have a React hook that takes, as input, a value that might be referentially unstable. You want to ignore updates to the value, but use the latest value when your code runs.

Solution:

js
function useFoo(unstableValue) {
  const ref = React.useRef(unstableValue);

  React.useLayoutEffect(() => {
    ref.current = unstableValue;
  });

  // use ref.current everywhere else here
}

Returning objects of state from a hook

SWR implements a variation of useState that provides fine-grained control over when re-renders occur. It's meant to be used in the implementation of other hooks that will return objects of state. The point is that updates to property's value in that state object will only trigger a re-render if the consumer of the hook is actually reading that property's value.

https://github.com/vercel/swr/blob/b58d7d06004b5f52d486c09ca6e9c2f62195d703/src/mutation/state.ts

Implement your custom hook using this pattern:

ts
function useMyHook() {
    // `stateRef` is a ref object whose `.current` value is the object that holds the state of type `S`.
    //
    // `stateDependencies` is a ref object with the same keys as `S`, but whose values are booleans
    // indicating whether they are being read by the consumer.
    //
    // `setState` is a `(newState: S) => void` callback to update the state. A re-render is triggered
    // _only if_ an individual key's state has changed _and_ the `stateDependencies` indicate that
    // it is being used.
    const [stateRef, stateDependencies, setState] = useStateWithDeps<S>(initialState);

    // TODO: Hook implementation...
    // - use `stateRef.current` for the current state...
    // - update the internal state with `setState`...

    // Finally, you should return a new state object for the caller to read using getter syntax:
    return {
        get foo() {
            stateDependencies.foo = true
            return foo
        }

        get bar() {
            stateDependencies.bar = true
            return bar
        }
    }
}

styled-components

A few tips relating specifically to the styled-components library.

Use data- attributes for conditional styles

If you want to apply a set of CSS rules conditionally, use a data- attribute selector.

jsx
import styled from "styled-components";

const SomeComponent = styled.div`
  [data-condition="true"] {
    // a bunch of CSS rules here...
  }
`;

export function MyComponent({ someCondition, children }) {
  return <SomeComponent data-condition={someCondition}>{children}</SomeComponent>;
}

TIP

This pattern is not only for booleans! You can do this for multi-valued data- attributes as well. This is good for setting up different style variants on a component, for example.

Use custom properties to avoid performance footguns

For every new value of a variable you interpolate in a styled component, it will generate a new classname and the corresponding block of styles to inject in your page and synchronously update the DOM to change the classname of affected elements. If your thing can have many possible values (such as when you're using an animation library to "tween" between two values), then this becomes unacceptable overhead.

Instead, define your styles using CSS custom properties and provide the value in the style prop of the styled component.

tsx
import { type ReactNode } from "react";
import styled from "styled-components";

const BackgroundDiv = styled.div`
  opacity: var(--opacity, 1);
`;

export function Background({ opacity, children }: { opacity: number; children: ReactNode }) {
  return <BackgroundDiv style={{ "--opacity": opacity }}>{children}</BackgroundDiv>;
}

Credit to Josh W. Comeau: https://www.joshwcomeau.com/css/styled-components/#css-variables-1

This work is licensed under CC BY-NC-ND 4.0