Neat Little React Tricks

Published Last updated v3.0.0

Here are some React tricks that are too small to warrant a separate blogpost, neat nonetheless.

JSX can ‘dynamic’, in the sense that the component type can be determined at runtime as long the variable name is capitalized 1. It’s just how JSX .

This can be useful when having to switch between two different components that take the same props, like variants of styled components:


_10
import { LeftSidebar, RightSidebar } from "./sidebars";
_10
_10
function Sidebar({ position, size, children }) {
_10
const SidebarComponent = position === "left" ? LeftSidebar : RightSidebar;
_10
_10
return <SidebarComponent size={size}>{children}</SidebarComponent>;
_10
}

Another real world use case is conditionally replacing an old component with a newly refactored one:


_16
// class-based or perhaps just wonky
_16
import { UserPermission as OldUserPermission } from "./old-user-permissions";
_16
// functional and clean
_16
import { UserPermission as NewUserPermission } from "./new-user-permissions";
_16
_16
function UsersManagement({ users, isNewAPI }) {
_16
// ...
_16
_16
// capitalized variable name!
_16
const UserPermissions = isNewAPI ? UserPermissions : NewUserPermissions;
_16
_16
return (<UserPermissions
_16
permission={permission}
_16
users={users}
_16
currentUser={currentUser}
_16
/>)

Yeah you could conditionally render the element with a as well but this is less work when the component has a lot of props and the props are the same for both.

Have some state that only ever updates in one particular way? Consider using a single-action useReducer over useState!

Take for example, a boolean in useState that can only be toggled. Maybe you’re already using a state updater function to avoid reading the state variable from component scope: setIsOpen((prevIsOpen) => !prevIsOpen). Knock it off and put that logic in a reducer instead, then just call the dispatch without any arguments.


_10
const [isOpen, toggleOpen] = useReducer((prevIsOpen) => !prevIsOpen, false);
_10
_10
// Flip the switch
_10
toggleOpen();

A nice benefit is that the is , whereas a callback for setting state is not. If a state setter-wrapping callback is called in a useEffect, it will have to be wrapped in useCallback to make it referentially stable. And that works but it’s quite some boilerplate for something as simple as flipping a switch.

Also, a reducer restricts what can be done to the state 2. Suppose you’re working in a team on a gigantic app and pass a state setter for a toggle down to parts of the app that fall under your teammates responsibilty. They can now set the state to whatever, which is not what a toggle should be able to do! OK, this might be slightly farfetched but you get what I mean, limiting possibilites is good design✨ .

Real simple, barely a trick but here you go. Just like setting default values on props, you can set a default for the special children prop 3. Set it to a sane default string or element and render children where you want.

I often use it on small reusable (styled) UI components, like buttons and links, that contain the same text most of the time, but need to overriden every now and then. I’m talking about text like “Read more…”, or “Open”,


_10
export const DonateLink = ({ children = "Donate" }) => (
_10
<Link href="/donate" className="styledLink">
_10
{children}
_10
<HeartIcon />
_10
</Link>
_10
);
_10
_10
// In practice:
_10
<DonateLink /> // default text
_10
<DonateLink>Donate now!</DonateLink> // overridden text

JSX is just another way to React.createElement, and are just plain objects 4. We can declare element variables before the return for reuse.

Here’s an example, it’s a component I use on this blog, it’s a fleshed out <blockquote> as seen on its MDN page. You can pass the component a url props which will both be set as the url attribute on the blockquote element and as the href on the inside the cite element. If no url is given, no anchor element should be inside the <cite>.


_13
export function Quote({ children, url, source }) {
_13
_13
const citeEl = <cite>{source}</cite>;
_13
_13
return (
_13
<figure>
_13
<blockquote cite={url}>{children}</blockquote>
_13
<figcaption>
_13
{url ? <a href={url} rel="noopener">{citeEl}</a> : citeEl}
_13
</figcaption>
_13
</figure>
_13
);
_13
}

Another use case could be to conditionally wrap an element with a tooltip.


_10
function TableCell({ value, isOutdated }) {
_10
const tableCell = <td>{value}</td>;
_10
_10
return isOutdated ? (
_10
<Tooltip text="This value is not up to date">{tableCell}</Tooltip>
_10
) : (
_10
tableCell
_10
);
_10
}

Take it a step further with React.cloneElement to conditionally add a prop. I think this is only worth if the wrapped element takes a lot of props, so those don’t have to be repeated. Repeating the element is fine if it’s a DOM node or a component without props.


_15
function TableCell({ value, setCell, isOutdated }) {
_15
const tableCell = (
_15
<EditableCell onChange={setCell} long={long} list={list} ofProps={ofProps}>
_15
{value}
_15
</EditableCell>
_15
);
_15
_15
return isOutdated ? (
_15
<Tooltip text="The value is not up to date">
_15
{cloneElement(tableCell, { className: "outdated" })}
_15
</Tooltip>
_15
) : (
_15
tableCell
_15
);
_15
}

First off, putting things in state that don’t belong there is a bad idea, you’ll have a hard time when you do so. Remember that state is reserved for data that changes over time 5.

There’s one exception to this rule and it’s using useState to initialize a value once, and it sure is a neat trick.

Why would we want to initialize a value once? Well, values defined in React components are reevaluated every render, that can be problematic. Sometimes it’s important that a value is initialized strictly once, like an external library. Other times, an object can be ‘expensive’ to create, for instance a huge array or heavy class. If you need to initialize such a value per component, useState can help.

First, the state value is guaranteed to be stable. On top of that, useState can take an initializer function to ensure the initial state is . The value should never change, so we can just ignore the state setter by not destructuring it.


_10
function Image(props) {
_10
const [intersectionObserver] = useState(() => new IntersectionObserver());
_10
_10
return (
_10
// JSX
_10
)
_10
}

Voila, a lazily-created, stable value.

“Isn’t this what useMemo is for?” No, not really. useMemo should be seen as a performance optimization for expensive computations based on , we shouldn’t rely on it for one-time initializations 6. Or as the docs put it:

useMemo lets you memoize an expensive calculation if the dependencies are the same. However, it only serves as a hint, and doesn’t guarantee the computation won’t re-run.

React Hooks FAQ - How to create expensive objects lazily?

Write your code so that it still works without useMemo — and then add it to optimize performance.

React Hooks Reference - useMemo

You can achieve the same with useRef, below is the example from How to create expensive objects lazily? section Hooks FAQ.


_14
function Image(props) {
_14
const ref = useRef(null);
_14
_14
// ✅ IntersectionObserver is created lazily once
_14
function getObserver() {
_14
if (ref.current === null) {
_14
ref.current = new IntersectionObserver(props.onIntersect);
_14
}
_14
return ref.current;
_14
}
_14
_14
// When you need it, call getObserver()
_14
// ...
_14
}

But I think it’s not as nice as useState with an initializer. I’d love for useRef to have an initializer function too but it doesn’t.

And those were all the tricks for this time!

  1. When an element type starts with a lowercase letter, it refers to a built-in component like <div> or <span> and results in a string ’div’ or ’span’ passed to React.createElement. Types that start with a capital letter like <Foo /> compile to React.createElement(Foo) and correspond to a component defined or imported in your JavaScript file. We recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter, assign it to a capitalized variable before using it in JSX. JSX In Depth - User-Defined Components Must Be Capitalized

  2. Also passing dispatch down is a better idea than passing a setter because it restricts what child can do. Dan Abramov tweet

  3. When you nest content inside a JSX tag, the parent component will receive that content in a prop called children. React Docs: Passing JSX as children

  4. An element is a plain object describing a component instance or DOM node and its desired properties. It contains only information about the component type (for example, a Button), its properties (for example, its color), and any child elements inside it. An element is not an actual instance. Rather, it is a way to tell React what you want to see on the screen. React Docs: React Components, Elements, and Instances - Elements Describe the Tree

  5. State is reserved only for interactivity, that is, data that changes over time. Thinking in React

  6. You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance. React Hooks Reference: useMemo