React’s state management tells how components interact and render dynamically. By leveraging hooks like useState
and using props
for state propagation, React manages UI updates seamlessly. Understanding its internal mechanism and external state management libraries integration will give more insights about its state management.
Where is the state stored?
React builds render tree for the component structure in your UI. State is tied to a position in the render tree.
How State Changes Affect the DOM Tree?
When
setState
oruseState
triggers a state update, React schedules a re-render for the affected component and mark it as dirty and queues it for reconciliationReact compares updater Virtual DOM with previous Virtual DOM and calculate the minimal set of changes needed
State updates generate new references for changed state objects, enabling React to detect changes during shallow comparisons.
React recursively renders components affected by the state change, updating their Virtual DOM nodes. Unaffected components are skipped, preserving their current state and DOM representation.
State management libraries and tools like Zustand*,* Redux (with Saga or Thunk), and Context API have distinct approaches to managing and propagating state changes. Subscription mechanisms are pivotal for how libraries like Zustand and Redux optimize React’s rendering by notifying components of state changes.
Subscription Mechanism
What?
A subscription mechanism allows components to “subscribe” to updates for specific parts of the state. When the state changes:
The library identifies which subscribers rely on the updated state.
It triggers re-renders only for those subscribers, enhancing performance.
How?
Registration: Components register as subscribers to specific state slices during mounting or first access.
State Update: Libraries track changes to specific state slices.
Notification: Only components relying on the affected state slices are notified.
Re-rendering: React re-renders notified subscribers, updating the DOM as needed.
Why?
Without Subscriptions (Naive Approach): All components relying on shared state re-render, even if their specific state hasn’t changed (e.g., basic Context API usage without optimization).
With Subscriptions: Only components dependent on changed state slices are re-rendered, reducing unnecessary updates and improving performance.
Zustand: Fine-Grained State Management
Zustand is a lightweight state management library designed for simplicity and performance. It enhances React’s state management by enabling fine-grained updates and selective component re-rendering. It uses a store-based model where state is shared among components, but the approach is different from Redux’s “store” and “actions” model.
How Zustand Handles State Changes
Centralized Store: Maintains a single store object in memory, created with the
create
function, and provides aset
function for updates. It acts as single source of truth for application stateState Update Flow:Calling
set
updates the internal Zustand state object and triggers a subscription mechanism to notify components of relevant state changes.Selective Re-rendering: Zustand subscribes components to specific state slices, ensuring only components relying on the changed slice re-render.Uses React’s
useSyncExternalStore
for efficient updates.React Integration: Components using
useStore
subscribe to specific parts of the Zustand store. On state changes, Zustand notifies React, triggering reconciliation and re-renders for affected components.Wiring Flow:
State Update (via
set
): Updates Zustand’s internal state.React Hook (via
useSyncExternalStore
): Listens for changes and triggers React’s reconciliation.React Renderer: Updates affected fiber nodes and the Virtual DOM.
Zustand’s useExternalSyncStore
useExternalSyncStore allows a component to subscribe directly to an external store, and whenever the external store updates, the component will re-render.
Zustand’s State Flow Diagram
Redux: Predictable state container
Redux is a state management library that stores the state of an application in a single, immutable JavaScript object called the store. The state is updated by reducers, which are pure functions that return a new state object based on the previous state and dispatched actions.
How Redux Updates State
Actions: When a user interacts with the UI, actions are dispatched. These actions contain information (e.g., type, payload) about the changes that need to happen to the state.
Reducers: Actions are sent to the reducers, which are responsible for updating the state immutably.
State Update: The new state object is returned, replacing the old state, and the UI reflects this change.
React-Redux
React-Redux is a library that makes it easy to connect your Redux store to React components. React-Redux builds on top of React’s Context API. The Context API allows us to share the Redux store throughout the component tree without manually passing props at every level. The store is provided to the entire app via Context.Provider
, and components can access the store using connect
or useContext
Here’s how the integration works:
Provider: The
Provider
component from React-Redux wraps the root of your application and provides the Redux store to all nested components. TheProvider
uses React's Context.Provider to make the store accessible to all child components.connect and useSelector: Components can use these methods to subscribe to the store.
connect()
: A higher-order component that binds Redux state and actions to a React component's props.useSelector()
: A React hook that allows you to extract state directly from the store in functional components.State Update Notification: When the Redux store updates, React-Redux checks if the subscribed state slices have changed. If they have, the component re-renders to reflect the new state.
React Redux Internals
The connect()
Function: A Higher-Order Component (HOC)
Store Subscription: When you call
connect()
, it subscribes the component to the Redux store. This means that any changes to the state will trigger a re-render of the connected component.State Mapping: The function
mapStateToProps()
is called to map pieces of the Redux store state to the component’s props. This allows components to access specific slices of state without having to manage global state manually.Dispatch Mapping: The function
mapDispatchToProps()
allows you to bind action creators to the component’s props, so the component can dispatch actions to modify the state.Shallow Comparison: When the Redux store updates, React-Redux performs a shallow comparison of the previous and next state. If the mapped state has changed, it will trigger a re-render of the component.
useSelector()
This hook allows you to read from the Redux store. It takes a selector function that receives the entire state and returns the part of the state you need.
const count = useSelector(state => state.count);
useDispatch()
This hook provides access to the dispatch
function, allowing you to dispatch actions to modify the Redux store.
const dispatch = useDispatch();
dispatch({ type: 'INCREMENT' });