new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

React Router · Guides · all subjects

state-management

15 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

State management is cache management in React Router

In React Router applications, state management primarily refers to synchronizing server state with the client. The server is the source of truth and client state functions as a cache. React Router's server-focused approach with loaders, actions, and forms with automatic revalidation makes most traditional client-side caching solutions redundant.

When to avoid traditional React state in React Router

Using traditional React state patterns for network-related data is an anti-pattern in React Router. This includes managing data from loaders, pending form submissions, or navigational states, as React Router already manages these through hooks like useNavigation, useFetcher, loaderData, and actionData.

Where to store state in React Router instead of React state

Data that developers might be tempted to store in React state has more natural homes in React Router: URL search params for state within the URL, cookies for small pieces of data stored on the user's device, server sessions for server-managed user sessions, and server caches for cached data on the server side.

Use Cache-Control headers for browser caching in loaders

React Router allows you to use Cache-Control headers within loaders to tap into the browser's native cache to avoid redundant data fetching. However, this approach has limitations and should be used judiciously. It is usually more beneficial to optimize backend queries or implement a server cache, as these changes benefit all users and eliminate the need for individual browser caches.

URL search params for toggling UI views

Use URL search params with HTML forms to manage UI state like list vs detail view. Instead of using React state and manually synchronizing with the URL, read and set state directly in the URL. This example shows a UI that toggles between list and detail views: import Form and useSearchParams from react-router, read view = searchParams.get('view') || 'list', and use a Form component with buttons that have name='view' and values 'list' or 'details'. This avoids state synchronization issues.

Three approaches to persistent UI state: React state, localStorage, and cookies

React state is simple and encapsulated but transient (doesn't survive page refreshes). localStorage persists across page refreshes and component mounts but requires synchronization and can cause UI flickering on server-side rendering because window and localStorage are unavailable during server rendering. Cookies provide the best user experience for persistent state: they work on the server for rendering and actions, eliminate state synchronization, persist across page loads and devices (if database-backed), and enable progressive enhancement. Cookies require more boilerplate and expose state beyond single components.

localStorage initialization in effects to avoid SSR errors

When using localStorage in React components that might be server-rendered, initialize state in a useLayoutEffect rather than directly in useState. Direct initialization like useState(window.localStorage.getItem('sidebar')) causes errors because window.localStorage is undefined during server rendering. Initializing in an effect avoids this, but creates potential for a mismatch between server-rendered state and localStorage, causing brief UI flickering after the page renders.

Cookies implementation for sidebar visibility state

To implement persistent UI state with cookies: First create a cookie object using createCookie('prefs'). In the loader, read the cookie from request.headers.get('Cookie'), parse it with prefs.parse(cookieHeader), and return the state. In the action, parse the cookie, read formData, update the cookie object, and return data(value, { headers: { 'Set-Cookie': await prefs.serialize(cookie) } }). In the component, use useFetcher(), access loaderData for initial state, and use optimistic UI by checking if fetcher.formData?.has('sidebar') to immediately update UI state. Render fetcher.Form with method='post' and buttons with name and value attributes.

Simplify form validation with React Router actions

Instead of managing separate React state for form fields, validation errors, and submission status, use React Router's action and actionData pattern. The action function validates on the server and returns errors. In the component, access actionData?.errors for error messages and navigation.formAction to detect if a specific form is submitting. Use the Form component instead of handling form submission manually. This eliminates the need for manual state synchronization, client-side validation logic duplication, and separate network handling, reducing complex components to a few lines of code.

Form validation example with React Router

This example shows a signup form using React Router: export async function action({ request }: ActionFunctionArgs) { const errors = await validateSignupRequest(request); if (errors) return { ok: false, errors }; await signupUser(request); return { ok: true, errors: null }; } export function Signup({ actionData }: Route.ComponentProps) { const navigation = useNavigation(); const userNameError = actionData?.errors?.userName; const passwordError = actionData?.errors?.password; const isSubmitting = navigation.formAction === '/signup'; return <Form method='post'><p><input type='text' name='username' />{userNameError ? <i>{userNameError}</i> : null}</p><p><input type='password' name='password' />{passwordError ? <i>{passwordError}</i> : null}</p><button disabled={isSubmitting}>Sign Up</button>{isSubmitting ? <BusyIndicator /> : null}</Form>; } The form works before JavaScript loads due to progressive enhancement.

Popular caching solutions that become redundant with React Router

Redux, TanStack Query, and Apollo are popular caching solutions in React. Redux is a predictable state container for JavaScript apps. TanStack Query provides hooks for fetching, caching, and updating asynchronous data in React. Apollo is a comprehensive state management library for JavaScript that integrates with GraphQL. With React Router's server-focused approach, the utility of these libraries becomes less prevalent, and most React Router applications forgo them entirely.

Track form dirty state with onChange handler

Track whether a form has unsaved changes by adding an onChange handler to the form element. Extract field values and set a isDirty boolean state based on whether any fields have content.

Clear form fields after successful submission

Use a useRef to hold a reference to the form element. After the action resolves successfully, call formRef.current?.reset() to clear all form field values.

Replace data with loaderData in meta functions

React Router v8 removed deprecated data fields in favor of loaderData. In meta functions, replace the data argument with loaderData. Replace matches[i].data with matches[i].loaderData.

Replace data with loaderData in useMatches hook

When calling useMatches(), replace matches[i].data with matches[i].loaderData to access loader data from matched routes.

Give your agent this brain