redux
state management
non-serializable data
JavaScript
web development

Redux - Where to keep non-serializable Data?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

The short answer is: keep non-serializable data out of Redux state whenever possible. Redux works best when the store contains plain serializable values such as objects, arrays, strings, numbers, and booleans. Live objects such as class instances, DOM nodes, promises, sockets, and mutable library handles usually belong in component state, refs, middleware, or dedicated service modules instead.

Why Redux Cares About Serializability

Redux tooling assumes state and actions can be inspected, copied, logged, persisted, and replayed.

That is why serializable state matters for:

  • Redux DevTools
  • persistence and rehydration
  • predictable debugging
  • time-travel inspection

If you put a WebSocket object, a function, or a Map full of mutable instances into the store, these workflows become harder or impossible.

What Should Stay In Redux

Redux should store the serializable facts about the world, not the live process object itself.

Good Redux state:

javascript
1{
2  connection: {
3    status: "connected",
4    lastMessageAt: 1710000000
5  },
6  uploads: {
7    byId: {
8      "u1": { progress: 60, status: "running" }
9    }
10  }
11}

Bad Redux state:

javascript
1{
2  socket: new WebSocket("wss://example.com"),
3  uploadController: someAbortController,
4  cache: new Map()
5}

The good version stores the app state you care about. The bad version stores live runtime objects that are hard to replay, serialize, or reason about.

Keep Live Objects In Middleware Or Services

A common home for non-serializable resources is middleware or a plain service module.

Example WebSocket manager:

javascript
1let socket = null;
2
3export function connectSocket(dispatch) {
4  socket = new WebSocket("wss://example.com");
5
6  socket.onopen = () => {
7    dispatch({ type: "connection/opened" });
8  };
9
10  socket.onmessage = (event) => {
11    dispatch({ type: "messages/received", payload: event.data });
12  };
13}
14
15export function sendMessage(text) {
16  socket?.send(text);
17}

Here Redux stores the connection status and received messages, while the actual socket instance lives outside the store.

That is the normal architecture.

Use React State Or Refs For UI-Local Non-Serializable Data

If the data belongs only to one component, keep it in component state or a ref rather than pushing it into Redux.

jsx
1import { useRef } from "react";
2
3function VideoPlayer() {
4  const videoRef = useRef(null);
5
6  function play() {
7    videoRef.current?.play();
8  }
9
10  return <video ref={videoRef} />;
11}

A DOM node or media element reference has no business in Redux. It is local UI runtime state, not application state.

Convert When A Serializable Representation Exists

Sometimes the non-serializable value has a clean serializable form.

Examples:

  • 'Date becomes an ISO string or timestamp'
  • 'Set becomes an array'
  • 'Map becomes a plain object or array of entries'

Example:

javascript
const createdAt = new Date();
const serializable = createdAt.toISOString();

This is often the best compromise: keep the store serializable, and reconstruct richer objects at the usage boundary when needed.

Redux Toolkit Warns You For A Reason

Redux Toolkit includes serializability checks in development. If you see warnings about non-serializable values in state or actions, the right first reaction is usually not to disable the check.

The better question is:

  • should this value live somewhere else
  • or should I convert it to a plain representation first

Only relax the check when you fully understand why the non-serializable value is present and why the tradeoff is acceptable.

A Good Rule Of Thumb

Store in Redux:

  • IDs
  • plain objects
  • arrays
  • status flags
  • timestamps or strings

Keep outside Redux:

  • sockets
  • promises
  • DOM nodes
  • class instances with behavior
  • mutable controllers and handles

That rule solves most of the ambiguity.

Common Pitfalls

The biggest mistake is putting live service objects into Redux because they are globally relevant. Global relevance does not automatically mean global state.

Another mistake is storing Date, Map, Set, or class instances directly when a plain serializable representation would work better.

People also disable Redux Toolkit's serializable warnings too quickly instead of treating them as architecture feedback.

Finally, do not confuse application state with process handles. Redux should store what happened and what the UI should reflect, not the imperative objects that make it happen.

Summary

  • Keep non-serializable runtime objects out of Redux state whenever possible.
  • Store serializable facts in Redux and keep live objects in middleware, services, refs, or component-local state.
  • Convert values such as Date, Set, or Map into plain serializable forms when practical.
  • Treat Redux Toolkit serializability warnings as useful design signals.
  • A good Redux store describes state, not the live machinery behind it.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.