UI Freezing when passing prop function from parent to child component

Summary

A React UI freeze caused by passing a callback from parent to child is almost always a symptom of unintended re-renders, state updates firing too often, or functions being recreated on every render. In this case, the parent component re-renders every time setmessage runs, which recreates the callback, which triggers the child to re-render, which triggers the table to re-render, eventually overwhelming the UI.

Root Cause

The freeze occurs because of a render loop triggered by state updates combined with heavy virtualization logic inside useMaterialReactTable.

Key contributing factors:

  • Parent state updates cause full parent re-render
  • Callback functions are recreated on every render
  • Child component receives a new function reference each time
  • Material React Table re-renders entire virtualized grid on every prop change
  • Row click triggers another state update, repeating the cycle

This creates a feedback loop of expensive renders that eventually stalls the UI.

Why This Happens in Real Systems

Real-world React systems often freeze when:

  • Components are computationally heavy (tables, charts, virtualized lists)
  • Props change frequently, especially function props
  • State updates occur inside event handlers that trigger deep re-renders
  • Developers forget to memoize callbacks or child components

Common triggers:

  • Passing inline functions as props
  • Updating state on every click without memoization
  • Using large datasets with virtualization libraries

Real-World Impact

When this pattern appears in production systems, it leads to:

  • UI freezes lasting seconds or indefinitely
  • High CPU usage due to repeated reconciliation
  • Unresponsive tables or lists
  • Users clicking repeatedly, worsening the freeze
  • Perceived app instability

Example or Code (if necessary and relevant)

Below is the corrected pattern using useCallback to prevent unnecessary re-renders:

const Support = () => {
  const [message, setMessage] = useState(null);

  const handleChildData = useCallback((data) => {
    setMessage(data);
  }, []);

  return (
    
  );
};

And memoizing the child:

export default React.memo(GridData);

How Senior Engineers Fix It

Experienced engineers apply several defensive techniques:

  • Memoize callback props using useCallback
  • Memoize child components using React.memo
  • Avoid passing unstable function references
  • Profile render cycles using React DevTools
  • Isolate heavy components so parent state changes don’t cascade
  • Move expensive logic outside render paths

They focus on stabilizing props so heavy components don’t re-render unnecessarily.

Why Juniors Miss It

Junior developers often overlook this issue because:

  • They assume React “just handles re-renders”
  • They don’t yet recognize that functions are new objects on every render
  • They underestimate how expensive virtualized tables can be
  • They focus on correctness, not render performance
  • They rarely use React DevTools to inspect render frequency

The pattern looks harmless, but in a real system with heavy components, it becomes a performance trap.

Leave a Comment