React Anti-Patterns

Last updated

Rough list of React anti—patterns with quotes I've compiled while reading docs, blog posts, and scrolling Twitter.

I think there are a lot of ways to write 'bad React' or 'React cut against the grain' that will still work, but will be needlessly complex, hard to read, more bug prone and just conceptually wrong. It might work but for the wrong reasons.

These are 'escape hatch' hooks, don’t overuse them.

  • useEffect
  • useRef
  • useMemo
  • useCallback

React state should be kept minimal, it’s a core principle of React for a reason. Introducing redundant states makes your app needlessly complex. You’re making multiple sources of truth and now you have to keep those in sync. Nothing stays in sync unless it’s forced to. With redundant states you’re creating places where bugs can creep in. it’s easier to keep a single source of truth and derive values from that. Keep the source data in state, derive what you need in render or even handlers. Don’t duplicate/sync states and don’t put non-state values in useState.

Derive, derive, derive!
Avoid state sprawl

Ask three questions about each piece of data:

  1. Is it passed in from a parent via props? If so, it isn’t state.
  2. Does it remain unchanged over time? If so, it isn’t state.
  3. Can you compute it based on any other state or props in your component? If so, it definitely isn’t state!
React Docs: Thinking in React - Find the Minimal but Complete Representation of UI State
React Docs: useState() - When not to use it

If you can calculate some information from the component’s props or its existing state variables during rendering, you should not put that information into that component’s state.


There are two types of “model” data in React: props and state. The two are very different:

  • Props are like arguments you pass to a function. They let a parent component pass data to a child component and customize its appearance. For example, a Form can pass a color prop to a Button.
  • State is like a component’s memory. It lets a component keep track of some information and change it in response to interactions. For example, a Button might keep track of isHovered state.
    Props and state are different, but they work together. A parent component will often keep some information in state (so that it can change it), and pass it down to child components as their props.
React Docs: Choosing the State Structure - Avoid redundant state

Next you’ll need to represent the visual states of your component in memory with useState. Simplicity is key: each piece of state is a “moving piece”, and you want as few “moving pieces” as possible. More complexity leads to more bugs!

React Docs: Reacting to Input with State: Step 3: Represent the state

The biggest problem with [duplicate states] is some of that state may fall out of sync with the true component state (squares). It could fall out of sync because we forget to update it for a complex sequence of interactions for example If you've been building React apps for a while, you know what I'm talking about. it’s no fun to have things fall out of sync. One thing that can help is to reduce duplication so that all relevant state updates happen in one place: We're no longer managing updating the squares state, so how do we keep these variables up to date? useEffect? Just call the state updaters when squares change right in the render method? I've seen people do all of these things... And none of them are great. The better way to do this is just to calculate it on the fly: (ie in render).

Kent C. Dodds: Don’t Sync State Derive it

The key [...] is that for any piece of data, you need to pick a single component that owns it as the source of truth, and avoid duplicating it in other components.

You Probably Don’t Need Derived State Brian Vaughn

Stuffing values that don’t need to be managed by React into state is more sneaky. It rarely results in visible bugs, but makes your components more complex and slows them down.

Thoughtspile: How to replace useState with useRef and be a winner

These examples [of redundant states] are all based on real-world code I’ve seen. And they all were in code that worked, the program executed as expected. But they’re still problematic. This type of derived state is error-prone; when new code is written to update the state, each new programmer touching the code has to understand the relationship, or the whole thing can get out of sync. When state is coming from props, like the error example above, you have to make sure that the state gets updated properly when the props change. And when the state gets out of sync, it is possible to end up in strange states where a programmer’s expectations are violated.


Just Derive State At Render Time. For many many things, including the 3 examples above, if you need information at render time that can be derived from other pieces of state, it’s totally fine to just calculate it at render time.


Storing derived information is unnecessary and harmful when calculating it at runtime adds minimal performance overhead or repetition.

Avoiding Derived State In React - benmccormick.org

useEffect has the dubious honour of being the most misunderstood and misused hook!

Maybe this is not so much about useState after all, but more about a misconception with useEffect: It should be used to sync your state with something outside of React. Utilizing useEffect to sync two react states is rarely right. So I'd like to postulate the following:


Whenever a state setter function is only used synchronously in an effect, get rid of the state!— TkDodo

TkDodo: Don’t over useState

[The default behavior of useEffect is not an infinite loop] unless you setState in it. the flaw is we failed to teach people that setState in it is usually a bad idea.



i went through 100+ examples at some point in the fb codebase, and about 30% of useEffect calls i’ve seen were unnecessary and/or wrong ways to approach a problem. this is partly an educational issue (we need to explain when not to use it) and partly the api being too generic.



it doesn’t help that typically it’s one of the first things people are taught. (by us as well.) so people get the idea that it’s an api you’re supposed to use often. useEffect is the last api you’re supposed to use — it’s the last resort when absolutely nothing else works.



one way to think about it is that useEffect is a way to synchronize React with some external system (network, subscription, DOM). if you don’t have any external system and just try to manage your data flow with useEffect, you’re gonna have all these problems.



when you synchronize with an external system, you absolutely do want it to re-run when something changes. that’s what synchronization means. synchronizing an extra time is never a problem except perf (which dependencies let you optimize).



another way to think about useEffect is that the goal is to have a more concrete dedicated Hook for each common concrete use case so that you almost never have to actually write useEffect yourself.



you can think of useEffect as either a synchronization “input” (subscribe to something -> receive data -> set state). or as a synchronization “output” (something changes -> write it somewhere, like to DOM). You only get loops if you start mixing the two, like short circuiting.

Dan Abramov tweet thread

In practice, you can make a lot of memoization unnecessary by following a few principles:

[...]

  • Avoid unnecessary Effects that update state. Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over.
  • Try to remove unnecessary dependencies from your Effects. For example, instead of memoization, it’s often simpler to move some object or a function inside an Effect or outside the component.
useMemo: Should you add useMemo everywhere?

Remember that the function passed to useMemo runs during rendering. doesn’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect, not useMemo.

React Docs Hooks Reference useMemo

useMemo() is not for side effects. It’s for pure computation. Resetting state based on other state is here: Hooks FAQ – How do I implement getDerivedStateFromProps?

Dan Abramov tweet

useCallback is for referential equality of functions (callbacks) in React components, often called ‘making a function stable’ or ‘stabilizing’. The principle that I go by for useCallback is that it’s a last resort, only use it if we really can’t solve it another way.

See Royi Codes: Death by a thousand useCallbacks

See Zhenghao: Preemptive Memoization In React Is Probably Not Evil (Yet)

Shallow comparison isn't free.

A common mistake is to think functions shouldn’t be dependencies. [It] seems like it could work. And to be clear, this code does work. But the problem with simply omitting local functions is that it gets pretty hard to tell whether we’re handling all cases as the component grows!


If we forget to update the deps of any effects that call these functions (possibly, through other functions!), our effects will fail to synchronize changes from our props and state. This doesn’t sound great.


Luckily, there is an easy solution to this problem. If you only use some functions inside an effect, move them directly into that effect:

A Complete Guide to useEffect: Moving Functions Inside Effects? — Overreacted

I want to emphasize that putting useCallback everywhere is pretty clunky. It’s a nice escape hatch and it’s useful when a function is both passed down and called from inside an effect in some children. Or if you’re trying to prevent breaking memoization of a child component. But Hooks lend themselves better to avoiding passing callbacks down altogether.

A Complete Guide to useEffect: Are Functions Part of the Data Flow? — Overreacted

The recommended fix is to move that function inside of your effect. That makes it easy to see which props or state your effect uses, and to ensure they’re all declared:

If for some reason you can’t move a function inside an effect, there are a few more options:

  1. You can try moving that function outside of your component*. In that case, the function is guaranteed to not reference any props or state, and also doesn’t need to be in the list of dependencies.

  2. If the function you’re calling is a pure computation and is safe to call while rendering, you may call it outside of the effect instead, and make the effect depend on the returned value.

  3. As a last resort, you can add a function to effect dependencies but**wrap its definition into the useCallback Hook. This ensures it doesn’t change on every render unless /its own/ dependencies also change:

React docs: Hooks FAQ — Is it safe to omit functions from the list of dependencies?

  • Is the function only used inside one effect? Yes? Move it in there.
  • Are you sure that making that one function stable will prevent a component from rerendering? Are the other values in that components props changing often between renders?
  • Does the useCallbacked function change with props that you’re also passing to a child component? I.e. are the useCallback deps also passed as props, fe example some state?
  • Does the function really need to be inside React? Does it use any information from the render scope? Does the function use anything from the data flow (props or state)? Can you move it out? Maybe by adding parameters for the information it is reading from render scope?
  • Does it make a big difference if the child rerenders? Is the child component (subtree) slow/expensive? Is the function defined high up in the component tree?
  • Can you use a state updater function to avoid a dependency?
  • Can you use a state updater module function instead of wrapping it in usecallback?
  • Can you use a useReducer and pass the dispatch function down? Can you use context module functions? It’s OK to use useReducer even if the state is small or medium-sized.

In the broader anti-pattern of optimizing too much, like too much useCallbacks and too many useMemos.

Just because it takes some work to figure out which ones to optimize, doesn’t mean we should turn it on everywhere. It will make the whole app slower. How is that possibly better? Shallow comparisons aren’t free. They’re O(prop count). And they only buy something if it bails out. All comparisons where we end up re-rendering are wasted. Why would you expect always comparing to be faster? Considering many components always get different props.

Dan Abramov tweet

See Zhenghao: Preemptive Memoization In React Is Probably Not Evil (Yet)

Placing state unnecessarily high up in tree can kill performance as a whole subtree needs to rerender. memo might help but it’s better to move to state down if possible. Place state as low as possible. You can even make a new component just to hold some state. See my post State Container Components for a practical example of this.

If you can’t find a component where it makes sense to own the state, create a new component solely for holding the state and add it somewhere in the hierarchy above the common parent component.

Thinking in React: Identify where your state should

Inline React components is when you’re declaring components inside of other components. This is one undisputed anti-pattern. It will instantiate a new React component on each render, leading to very weird behavior and bad performace. There's an ESLint rule for this in eslint-plugin-react that I recommend you turn on: react/no-unstable-nested-components.

Components can render other components, but you must never nest their definitions: The snippet above is very slow and causes bugs. Instead, define every component at the top level: When a child component needs some data from a parent, pass it by props instead of nesting definitions.

React Docs: Your First Component — Nesting and organizing components

Element type changes (reference equality, current.elementType === element.type):
-> Reconciliation sees it as a new component and replaces it and its children
-> Children lose their current state

Codepen Demo demonstrating the problem

No, [nesting components] is not supported (and has never been) and will reset state of the entire children tree every time the parent re-renders. we should add a lint rule or something. generally we haven’t been proactive warning about it because it’s assumed to be clear that something is wrong — the entire tree will be reset including all of its state so you’re bound to notice it. is that not the case in your experience? how do people miss this?

Tweet by Dan Abramov in response to question on whether inline components are bad

The point of components is that we let React decide when to call them.

You might be wondering: why don’t we just call components directly? Why write <Form /> rather than Form()?


React can do its job better if it “knows” about your components rather than if it only sees the React element tree after recursively calling them.

React as a UI Runtime — Inversion of Control

If we called Comments() as a function, it would execute immediately regardless of whether Page wants to render them or not
[...]
But if we pass a React element, we don’t execute Comments ourselves at all:
[...]
This lets React decide when and whether to call it. If our Page component ignores its children prop and renders <h1>Please log in</h1> instead, React won’t even attempt to call the Comments function. What’s the point? This is good because it both lets us avoid unnecessary rendering work that would be thrown away, and makes the code less fragile. (We don’t care if Comments throws or not when the user is logged out — it won’t be called.)

React as a UI Runtime — Inversion of Control

I recommend reading the rest of the section in that blog post.

Another good read on this is Don’t call a React function component.

https://stackoverflow.com/questions/61280400/defining-a-sub-render-function-in-render-vs-a-react-component

https://stackoverflow.com/questions/64769304/advantages-of-rendering-a-component-vs-a-function-returning-jsx-react

While this will work ‘fine’, it’s counter to React’s idea/convention that every function that returns React elements to render should be a component. One that has a capitalised name, receives a single object parameter: props and is written in JSX. I’m not sure where this comes from, an aversion to making small component? It’s OK to extract it to a child component. If it’s enough to warrant a separate function, why not extract it to a component so we’re not cutting against the grain? Relying on closure makes it implicit what data the function has access to. In contrast, props make this explicit. That’s what props are for.

If your components get messy with too much nested conditional markup, consider extracting child components to clean things up

React Docs: Conditional Rendering

This is like a bastardised pseudo-component. It’s a function that returns a React element, but it doesn’t take props and can’t be rendered using JSX. It’s also usually defined /before/ the render method, so you end up reading some random child elements out of context before you know where they go in the parent.


I honestly can’t see any reason to use this pattern over a simple function component. A component can use hooks, can be memoised, is more idiomatic React—the framework gives you a specific way for modularising your render, so why not use it?

Stop exporting things, I’m begging you – Say no to weird render functions | Oliver Phillips - Frontend Engineer

If you’re repeating yourself in the return, you can assign JSX to a variable up front (an element variable) and use that in multiple places, and even pass it on as props!

When you need to extract markup from a component or logic, don’t put it in a function living in the same component. A component is just a function. Defining it this way is nesting inside its parent. This means that it will have access to all the state and data of its parent. It makes the code more unreadable - what is this function doing in between all the components? Move it in its own component, name it and rely on props instead of a closure.

Tao of React - Avoid nested render functions | Alex Kondov

Too many props will make your component less readable and less understandable This is a sign of ill-composed components, which is another anti-pattern.

Usually, you will pass information from a parent component to a child component via props. But passing props can become verbose and inconvenient if you have to pass them through many components in the middle, or if many components in your app need the same information


Passing props is a great way to explicitly pipe data through your UI tree to the components that use it. But it can become verbose and inconvenient when you need to pass some prop deeply through the tree, or if many components need the same prop. The nearest common ancestor could be far removed from the components that need data, and lifting state up that high can lead to a situation sometimes called “prop drilling


Context is very tempting to use! However, this also means it’s too easy to overuse it. Just because you need to pass some props several levels deep doesn’t mean you should put that information into context. Here’s a few alternatives you should consider before using context:

  1. Start by passing props . If your components are not trivial, it’s not unusual to pass a dozen props down through a dozen components. It may feel like a slog, but it makes it very clear which components use which data! The person maintaining your code will be glad you’ve made the data flow explicit with props.
  2. Extract components and pass JSX as children to them. If you pass some data through many layers of intermediate components that don’t use that data (and only pass it further down), this often means that you forgot to extract some components along the way.
React Docs: Before You Use Context

What good is it to render the component when it cant do anything yet?

I don’t like living in ‘data limbo’, having to check and handle the value being undefined or null at every turn gets tedious real fast. That's a lot of additional checks for nothing. Save yourself the pain and just hold off rendering until you have everything you need (or most of it).

I go by: when the data is not the ready, the component is not ready. Maybe I subconsiously got that from someone else and forgot.

I view TypeScript optional ? for optional props, should not be used for signifying that data possible isn’t defined yet.

An accumulating hook is one that returns some data consumed from another custom hook. One hook should be the owner of some data, much like how one component should be the owner of some state in React.

Compare


_17
function SubmitButton({ status, isNew }) {
_17
const isCreating = status === "creating";
_17
const isUpdating = status === "updating";
_17
_17
return (
_17
<button
_17
name="intent"
_17
type="submit"
_17
value={isNew ? "create" : "update"}
_17
disabled={isCreating || isUpdating}
_17
_17
>
_17
{isNew ? (isCreating ? "Creating..." : "Create") : null}
_17
{isNew ? null : isUpdating ? "Updating..." : "Update"}
_17
</button>
_17
)
_17
}

with


_24
function SubmitButton({ status, isNew }) {
_24
const isCreating = status === "creating";
_24
const isUpdating = status === "updating";
_24
_24
return isNew ? (
_24
<button
_24
name="intent"
_24
type="submit"
_24
value="create"
_24
disabled={isCreating}
_24
>
_24
{isCreating ? "Create" : "Creating..."}
_24
</button>
_24
) : (
_24
<button
_24
name="intent"
_24
type="submit"
_24
value="update"
_24
disabled={isUpdating}
_24
>
_24
{isUpdating ? "Update" : "Updating..."}
_24
</button>
_24
);
_24
}

The first is shorter but harder to follow. In my opinion, it’s nicer to switch on isNew and write two <button> elements.

See also Good advice on JSX conditionals.