Sitemap
REIT monero

REIT monero

How to Simplify State Management with Valtio

4 min readAug 20, 2026

--

Recommended architecture

UI / React components
↓
useSnapshot(state)
↓
Valtio store
↓
actions / mutations
↓
API / persistence / side effects

A practical project structure:

src/
├── state/
│ ├── store.ts # Root Valtio state
│ ├── actions.ts # State mutations
│ ├── selectors.ts # Derived/read-only state
│ └── types.ts
├── features/
│ ├── auth/
│ ├── projects/
│ └── tasks/
├── components/
└── services/
├── api.ts
└── persistence.ts

Valtio pattern

import { proxy } from "valtio";
export const appState = proxy({
user: null,
projects: [],
ui: {
activeProjectId: null,
isLoading: false,
},
});
export const actions = {
setUser(user) {
appState.user = user;
},
addProject(project) {
appState.projects.push(project);
},
setActiveProject(id) {
appState.ui.activeProjectId = id;
},
};

Components should primarily read state:

const snap = useSnapshot(appState);
return <div>{snap.projects.length} projects</div>

And mutations should go through the action layer rather than being scattered throughout components.

Project workflow

1. Model the state

First identify:

  • server state
  • client/UI state
  • derived state
  • transient state

Don’t put everything into Valtio. For example, API-cache/server-state concerns may be better handled by TanStack Query, while Valtio handles application/client state.

2. Create the minimal store

Start with the smallest state model that represents the application’s domain.

3. Centralize mutations

Create actions such as:

createProject()
updateProject()
deleteProject()
selectProject()
setUser()
resetState()

This prevents components from becoming state-management layers.

4. Separate side effects

Keep API calls, persistence, analytics, etc. outside the core state model:

Component
↓
Action
↓
Service/API
↓
Action updates Valtio state

5. Use snapshots at the UI boundary

React components consume useSnapshot() and should generally avoid knowing how the underlying state is mutated.

6. Add derived state only when necessary

Prefer computed/derived values over duplicating information:

projects
↓
selectedProject
↓
visibleTasks

rather than storing all three independently.

7. Test actions independently

The most valuable tests should verify state transitions:

initial state
↓
createProject()
↓
project exists
↓
selectProject()
↓
activeProjectId updated

Simplify State Management with Valtio

You are working on a TypeScript/React application that uses Valtio for client-side state management.

Objective

Refactor the application’s state management so that it is:

  • simple and predictable
  • centralized without becoming monolithic
  • strongly typed
  • easy to test
  • easy for React components to consume
  • resistant to duplicated or inconsistent state

Architecture

Use the following separation of responsibilities:

React Components
↓
useSnapshot()
↓
Valtio State
↓
Actions
↓
Services / APIs / Persistence

State

Keep domain and UI state in Valtio proxies.

Actions

Put state mutations behind explicit action functions.

Examples:

  • createProject()
  • updateProject()
  • deleteProject()
  • selectProject()
  • setUser()
  • resetState()

Actions should contain state-transition logic but should not contain unnecessary UI concerns.

Services

Keep external side effects separate from the state model.

Get REIT monero’s stories in your inbox

Join Medium for free to get updates from this writer.

Examples:

  • HTTP/API requests
  • localStorage persistence
  • analytics
  • authentication providers

When an asynchronous operation changes application state, use the service to perform the operation and the action/store layer to apply the resulting state transition.

Rules

  1. Do not duplicate the same piece of state in multiple places.
  2. Prefer derived values over storing redundant state.
  3. Do not introduce a global store for state that is truly local to one component.
  4. Do not put API-cache management into Valtio if an existing server-state solution already handles it.
  5. Keep Valtio state strongly typed.
  6. Keep mutation logic out of presentational components where practical.
  7. Avoid unnecessary abstractions, wrappers, and generic state-management utilities.
  8. Preserve existing application behavior while refactoring.
  9. Make the smallest safe change necessary.
  10. Do not rewrite unrelated parts of the codebase.

Workflow

Phase 1 — Inspect

Before changing code:

  • inspect the existing state-management implementation
  • identify all stores and duplicated state
  • identify components that directly mutate state
  • identify server state versus client state
  • identify derived state
  • identify existing persistence/API mechanisms
  • identify tests covering state behavior

Do not modify code during this phase.

Phase 2 — Design

Propose:

  • the Valtio state shape
  • action boundaries
  • derived-state strategy
  • service boundaries
  • migration strategy

Explain any important trade-offs briefly.

Wait for confirmation if the proposed refactor is architecturally significant.

Phase 3 — Implement

Implement incrementally.

For each change:

  1. update types
  2. update Valtio state
  3. introduce or update actions
  4. migrate consumers
  5. remove obsolete state
  6. update tests

Avoid a large all-at-once rewrite.

Phase 4 — Verify

After implementation:

  • run TypeScript checks
  • run linting
  • run relevant unit/integration tests
  • search for obsolete store usage
  • search for duplicated state
  • verify that components still render correctly
  • verify async/error/loading transitions

Report:

Changed:
- ...
Removed:
- ...
Tests:
- ...
Remaining risks:
- ...

Definition of Done

The refactor is complete when:

  • Valtio is the clear source of truth for the intended client state
  • mutations are centralized in understandable actions
  • React components primarily consume snapshots
  • duplicated state has been removed
  • server state remains appropriately separated
  • TypeScript passes
  • tests pass
  • no unrelated behavior has changed
  • the resulting architecture is simpler than the original one

When uncertain, prefer the simplest implementation that preserves existing behavior.

--

--