React Refresh Triggers

Posted Thursday, March 27, 2025 by Sri. Tagged MEMO
EDITING PHASE:gathering info...

From what I gather, React initially renders a component tree with starting values. When particular values or events occur, this triggers a rerender after running the reconciliation algorithm to see what changed, marking them for a rerender.

High-level Rendering Overview

Every component exports a render function that is compiled JSX. Components are rendered from the root on down through its children using provided props

DIRECT RENDER EVENTS

  • Components are rerendered when any of its props change. All child components are also rerendered.
  • Components in an array are rerendered when their unique key prop changes.
  • Components that refer to state or context in their render functions are also rerendered when they change through the useState and useContext hooks.

INDIRECT RENDER CHANGES

  • Hooks that use dependencies (e.g. useEffect) will run whenever the dependency changes. The body of the hook can then change state or context to affect the component.
  • Contexts that contain state hooks (e.g. useState) provides a way for components to use them. The context value doesn't change, and neither do its properties.

CHANGE DETECTION ALGORITHM

  • React uses simple object equality (by reference) and shallow comparisons (by value, no nested objects) to detect changes.

Sources of Refresh Events

The tables below are based on Claude Sonet query, "Provide a list of React's refresh triggering mechanisms for the built-in hooks, props for functional components." so there's a good chance it's not completely right.

In general, the refresh process starts with the component that triggered it and its subtree of components also rerenderModified by memoization through useMemo..

Mechanism Refresh Behavior
State Change Component refreshes when internal state is updated via setState or Hook state setters
Props Change Component refreshes when received props change (props with objects use reference equality)
Parent Render Component refreshes when parent component renders (unless wrapped in memo)
Context Change Component refreshes when the value of the context contains useState getters
Key Change Complete unmount/remount cycle when key prop changes
React.memo Prevents refresh when props actually change
Reducer Updates Component refreshes when a reducer dispatch function is called with an action
Force Update Class components refresh when forceUpdate() is called
External Store Component refreshes when subscribed external store changes via useSyncExternalStore
Concurrent Features Updates from useDeferredValue or useTransition completing their work (?)
Error Boundaries Components refresh when an error is caught or reset
Suspense Resolution Components refresh when suspended data becomes available

Note that React compares objects consistently throughout its system using reference equality.

Refresh-related Hooks

Hook Refresh Trigger Mechanism
useState Component refreshes when state setter function is called with a new value (reference or primitive)
useReducer Component refreshes when dispatch function is called, resulting in a new state
useContext Component refreshes when the context value changes (based on reference equality)
useCallback Returns memoized function; dependencies array change creates new function reference
useMemo Returns memoized value; recalculates only when dependencies change
useDeferredValue Creates deferred version of value that executes asynchronously
useSyncExternalStore Subscribes to external store; refreshes when store changes

Other Hooks

Hook Effect Trigger Mechanism
useEffect No direct refresh; triggers side effects after render
useLayoutEffect No direct refresh; triggers synchronous side effects before browser paint
useRef No refresh when .current property changes; maintains reference across renders
useImperativeHandle No direct refresh; customizes instance value exposed to parent components
useDebugValue No refresh; used for displaying custom hook values in React DevTools
useTransition No direct refresh; marks state updates as transitions to avoid blocking UI
useId No refresh; generates stable, unique IDs across server/client

Reconciliation Algorithm

React's reconciliation process follows these sequential steps:

  1. Tree Comparison: React compares the current virtual DOM tree with the new one generated after state or prop changes.

  2. Element Type Evaluation: React first checks if the element type has changed. If the type differs (e.g., from div to span), React discards the old subtree and builds a new one.

  3. Key-Based Reconciliation: For lists of elements, React uses the key prop to track which items have been added, removed, or repositioned rather than re-rendering the entire list.

  4. Component Instance Preservation: When element types match, React updates only the props of the existing component instance, maintaining internal state.

  5. DOM Update Batching: Once differences are identified, React batches DOM updates to minimize reflow and repaint operations.


Appendix: Writing Custom Hooks

import { useState, useEffect } from 'react';

function useCustomHook(initialValue) {
// State management
const [value, setValue] = useState(initialValue);

// Side effects
useEffect(() => {
// Effect logic
return () => {
// Cleanup logic (optional)
};
}, [dependencies]);

// Additional logic or transformations

// Return values and/or functions
return { value, setValue };
}
export default useCustomHook;

Note that the return value is not limited to the patterns established by useEffect or useState; you can return anything that makes use of the built-in hooks to do React-related stuff.

Appendix: Difference Between Class and Functional Components

Class Components

Class Components use lifecycle methods to explicitly orchestrate lifecycle events:

  • The constructor receives props from <Component prop1 prop2 />
  • State is initialized in the constructor by directly setting this.state. Changes afterwards must be done through this.setState() so reconciliation of changes can run.
  • Class methods componentDidMount() and componentDidUnmount() for initialization and garbage collection
  • The render() method returns JSX that made use of this.state and any passed props.

The source of the render trigger can be local or from a parent:

  • local this.setState() will trigger a local rerender based on what's in the render() function.
  • When the parent rerenders due to state or prop change, all its children also rerender.

Functional Components

Functional Components, by comparison, use an implicit lifecycle, and rely on hooks to trigger rerenders.

  • The function receives a props object
  • The function returns JSX as its render function, which as in Class Components will references to any props that are used.
  • State is provided through the useState hook, which provides the syntactic sugar to make state look like a regular variable reference, and providing a separate setter function that serves as the trigger for reconciliation
  • Lifecycle mount/unmount behavior is encapsulated by the useEffect hook, which can return a "cleanup" function to run when the function is unmounted. There can be more than one such effect hook.