#
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
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 lowerstaleTime(i.e. 30s or even 0s). If the data is unlikely to change often, set a highstaleTime, even up toInfinityto ensure it never refreshes.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.