What Are ActionTypes.INIT and ActionTypes.REPLACE in Redux? Internal Lifecycle Explained
ActionTypes.INIT and ActionTypes.REPLACE are private, internally-generated action types used by Redux to initialize store state and handle hot module replacement, respectively.
These reserved action types live in the reduxjs/redux repository and are never meant to be dispatched by application code. Instead, they power critical lifecycle moments: ActionTypes.INIT triggers initial state population when you call createStore(), while ActionTypes.REPLACE enables seamless reducer swapping during code splitting and hot reloading.
What Are ActionTypes.INIT and ActionTypes.REPLACE?
Redux generates two unique string identifiers in src/utils/actionTypes.ts to prevent collisions with user-defined actions:
@@redux/INIT${randomString()}– Signals store creation and initial state gathering@@redux/REPLACE${randomString()}– Signals that the root reducer has been swapped viastore.replaceReducer()
Both strings receive a random suffix per library instance, ensuring uniqueness across multiple Redux builds on the same page.
How These Action Types Are Generated
In src/utils/actionTypes.ts, Redux constructs these constants using a randomString() helper:
const ActionTypes = {
INIT: `@@redux/INIT${/* #__PURE__ */ randomString()}`,
REPLACE: `@@redux/REPLACE${/* #__PURE__ */ randomString()}`,
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
};
The /* #__PURE__ */ annotation helps bundlers with tree-shaking, while the random suffix prevents accidental handling of these actions in user reducers.
How ActionTypes.INIT Initializes Redux Store State
When you invoke legacy_createStore (or createStore), Redux immediately dispatches ActionTypes.INIT to populate the initial state tree. This happens in src/createStore.ts at approximately line 395:
// src/createStore.ts
dispatch({ type: ActionTypes.INIT } as A);
Each reducer in your application receives this action. If a reducer receives state === undefined, it must return its default initial value. This mechanism allows Redux to build the complete state tree without requiring you to manually pass a preloaded state object.
combineReducers and INIT Validation
The combineReducers utility in src/combineReducers.ts uses ActionTypes.INIT to validate reducer shape during store creation. In the assertReducerShape function (lines 66-84), Redux calls each child reducer with undefined and the INIT action:
function assertReducerShape(reducers) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key];
// Verify reducer returns defined state for INIT
const initialState = reducer(undefined, { type: ActionTypes.INIT });
// Verify reducer handles unknown actions safely
reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION() });
});
}
This ensures every slice reducer properly initializes when called with undefined and does not throw on unknown action types.
How ActionTypes.REPLACE Enables Hot Module Replacement
When you call store.replaceReducer(newReducer)—commonly used in hot module replacement (HMR) scenarios—Redux swaps the internal root reducer reference and dispatches ActionTypes.REPLACE. This occurs in src/createStore.ts at approximately lines 45-46:
// src/createStore.ts (replaceReducer implementation)
dispatch({ type: ActionTypes.REPLACE } as A);
The REPLACE action signals to the new reducer tree that a replacement has occurred. Existing slice reducers that persist across the swap receive their previous state, while newly added slices receive undefined and initialize themselves. This allows state to survive code-splitting or hot-reloading events without full store recreation.
Suppressing Warnings During Reducer Replacement
combineReducers specifically checks for ActionTypes.REPLACE to avoid false-positive "unexpected key" warnings. When reducers are replaced during HMR, the state shape might legitimately change. In src/combineReducers.ts, the utility skips validation when it detects the replace action:
if (action && action.type === ActionTypes.REPLACE) {
return; // Skip unexpected key checks during replacement
}
This ensures developers don't see spurious warnings when legitimately swapping reducer logic in development environments.
Summary
- ActionTypes.INIT initializes the Redux store by dispatching during
createStore(), forcing each reducer to return its default state when called withundefined. - ActionTypes.REPLACE facilitates hot module replacement by signaling reducer swaps via
store.replaceReducer(), allowing state persistence across code splits while suppressing validation warnings. - Both action types are generated in
src/utils/actionTypes.tswith random string suffixes to prevent collisions and are consumed insrc/createStore.tsandsrc/combineReducers.ts. - These are private implementation details—never dispatch them manually or handle them explicitly in your reducers.
Frequently Asked Questions
Can I dispatch ActionTypes.INIT or ActionTypes.REPLACE manually?
No. These action types are reserved for internal Redux use only. Dispatching them manually could break state initialization logic or trigger unexpected behavior during reducer replacement. The Redux source code explicitly warns: "Do not reference these action types directly in your code."
Why do these action types have random strings appended?
The random suffixes (generated by randomString() in src/utils/actionTypes.ts) ensure uniqueness across multiple Redux instances on the same page. This prevents action type collisions if you load Redux from different bundles or if third-party libraries also use Redux internally.
How does ActionTypes.REPLACE differ from manually re-creating the store?
store.replaceReducer() preserves the existing state tree and subscription listeners, whereas creating a new store (createStore(newReducer)) destroys both. ActionTypes.REPLACE enables hot module replacement by allowing the new reducer to initialize only its new slices while keeping existing state intact.
Where are these action types defined in the Redux source code?
They are defined in src/utils/actionTypes.ts as properties of the ActionTypes object. The INIT and REPLACE strings are consumed in src/createStore.ts during store initialization and reducer replacement, and in src/combineReducers.ts for state shape validation and warning suppression.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →