# State Management

Historical information and opinions/decisions about how we manage state within a React application.

# Mantras

  • To retain across page reloads, use URL searchParams
  • Appropriately cache asynchronous data to avoid unnecessary network calls
  • Start simple, build layers as needed
  • Avoid prop drilling
  • Keep state close // TODO: maybe reword this

# Asynchronous State

Data that is fetched from outside of our local application, typically via an API accessed with Network requests, is known as asynchronous state. We fetch this with an API layer (Axios), store it in a caching layer (Tanstack Query), and then delivery it to our application components via custom hooks

Cache invalidation is one of the most difficult parts of a web app. Luckily we have a set of very intuitive tools and levers to pull in Tanstack Query. The main things we want to be considerate of in our async state management with Tanstack Query are

  1. staleTime - The time in milliseconds after data is considered stale. This can be set globally, or at time of usage. Can vary wildly depending on the type of content. If data will update frequently, set a lower staleTime (i.e. 30s or even 0s). If the data is unlikely to change often, set a high staleTime, even up to Infinity to ensure it never refreshes.
  2. cacheTime - The time in milliseconds that unused/inactive cache data remains in memory. Think of this as a garbage collection expiry. How long do we want to keep data that has not been used by a component in memory before trashing it and refetching it the next time it is requested.

# Local State

React provides a myriad of options for storing state locally in our components. These are mostly via hooks (useState, useReducer, useRef) and each have their own pros/cons/tradeoffs.

# Semi-Local State

For state that is needed across multiple components, we typically reach for one of three things:

# 1. URL searchParams

One of the easiest way to share a piece of data across components is via searchParams. This will store the data in the URL, allowing it to be accessed from any component. With React Router, we have access to useSearchParams() hook that provides a friendly API for this interaction.

However, this should not be abused. Only store data in the URL that is necessary to be accessed after a page reload, when a user saves the URL as a bookmark, or copy/pastes it to another user.

# 2. Composition

Often we can avoid prop-drilling with a simple re-imagining of the components structure. This can be referred to as Composition, and is extremely helpful in sharing state/data across nested components. It involves using the children prop (or any explicit prop), to share data to components that might otherwise be deeply nested and avoid prop drilling.

const Title = ({ title }) => <h1>{title}</h1>;

const Header = ({ title, children }) => {
  return (
    <header>
      {title}
      <div>Some other internal content</div>
      {children}
    </header>
  );
}
function App() {
  const [title, setTitle] = useState('Hello World');

  return (
    {/* can pass a component as a prop */}
    <Header title={<Title title={someState} />}>
      {/* or pass a component as a children */}
      <Navigation title={title} />
    </Header>
  );
};

# 3. Context

React provides a build in way to manage state across multiple components, called Context. Context lets the parent component make some information available to any component in the tree below it—no matter how deep—without passing it explicitly through props.

You can store any data you want, as well as data setters, in your Context value. It is common to use a reducer together with context to consolidate a the state update logic.

# Honorable Mention

One new state management option that we have yet to utilize, but deserves an honorable mention is Zustand. Zustand is a small and fast bare bones state-management solution that uses simplified flux principles.

It gives you much of the flexibility and patterns of Redux, but without the boilerplate. In the event that a Context based solution becomes unwieldy or feels like too much boilerplate to setup, a small, dedicated Zustand store might be a good fit.