How to Call a Parent Method from a Child Component in React

Beginner8 min interview
Skills tested:
Passing a callback function from parent to child as a propCalling the callback in the child with data to pass back to the parentTyping callback props correctly in TypeScriptUsing useCallback to stabilize callback references and prevent unnecessary child re-rendersKnowing when callbacks are the right approach vs lifting state vs using context

Advertisement

🧩 Scenario

Child-to-parent communication via callbacks is one of the most common patterns in React. Form submissions, list item selection, modal close events, and search input changes all require a child to notify a parent of something. Understanding this pattern correctly, including type safety and reference stability, is a prerequisite for most React interview questions about component interaction.

Architecture Walkthrough

One-Way Data Flow and Callbacks

React enforces one-way data flow: data flows from parent to child via props, and events flow from child to parent via callback props. A child component cannot reach into its parent and modify state directly. Instead, the parent passes a function as a prop. The child calls that function, optionally passing data as arguments. The parent receives the call and updates its own state.

This design keeps data ownership explicit. The state always lives in the component that controls it. The child is a controlled component that signals intent to the parent but does not manage the shared data itself.

TypeScript Callback Prop Typing

In TypeScript, callback props are typed as function signatures. A callback that receives an item ID and returns nothing is (id: string) => void. A callback that receives a form object is (data: FormData) => void. Typing these explicitly ensures the child passes the right arguments and the parent handles the right shape.

Reference Stability with useCallback

When a parent passes a callback defined as an inline arrow function in JSX, a new function reference is created on every parent render. If the child is wrapped in React.memo, this new reference causes the child to re-render even when the actual behavior of the callback has not changed. Wrapping the callback in useCallback with appropriate dependencies stabilizes the reference: the same function object is reused across renders unless the dependencies change.


Key Code Explained

// Basic pattern: parent passes callback, child calls it
interface DeleteButtonProps {
  itemId: string;
  onDelete: (id: string) => void; // typed callback prop
}

function DeleteButton({ itemId, onDelete }: DeleteButtonProps) {
  return (
    <button
      className="btn-destructive"
      onClick={() => onDelete(itemId)} // calls parent's handler with data
    >
      Delete
    </button>
  );
}

function ItemList() {
  const [items, setItems] = useState<Item[]>(initialItems);

  // useCallback: stable reference across renders
  const handleDelete = useCallback((id: string) => {
    setItems((prev) => prev.filter((item) => item.id !== id));
  }, []); // no deps: setItems is stable, filter logic has no external deps

  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.name}
          <DeleteButton itemId={item.id} onDelete={handleDelete} />
        </li>
      ))}
    </ul>
  );
}


// Passing a complex object from child to parent
interface SearchFilters {
  query: string;
  category: string;
  minPrice: number;
}

interface FilterPanelProps {
  onFiltersChange: (filters: SearchFilters) => void;
}

function FilterPanel({ onFiltersChange }: FilterPanelProps) {
  const [query, setQuery] = useState('');
  const [category, setCategory] = useState('all');
  const [minPrice, setMinPrice] = useState(0);

  const handleApply = () => {
    // Pass all filter data as a typed object back to the parent
    onFiltersChange({ query, category, minPrice });
  };

  return (
    <div>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <select value={category} onChange={(e) => setCategory(e.target.value)}>
        <option value="all">All</option>
        <option value="electronics">Electronics</option>
      </select>
      <button onClick={handleApply}>Apply Filters</button>
    </div>
  );
}

function SearchPage() {
  const [filters, setFilters] = useState<SearchFilters>({
    query: '',
    category: 'all',
    minPrice: 0,
  });

  // Parent receives the filters object and updates its own state
  const handleFiltersChange = useCallback((newFilters: SearchFilters) => {
    setFilters(newFilters);
  }, []);

  return (
    <div>
      <FilterPanel onFiltersChange={handleFiltersChange} />
      <ResultList filters={filters} />
    </div>
  );
}

The useCallback with an empty dependency array on handleDelete is safe here because the function only calls setItems (which is stable across renders from useState) and uses the functional updater form (prev => ...). If the callback closed over a state or prop value directly (like items instead of using the updater form), it would need that value in the dependency array.


Tradeoffs

Communication methodDirectionWhen to use
Callback propsChild to parent1-2 levels deep, localized component communication
Prop drilling callbacksChild to ancestorWorks but creates coupling; avoid beyond 2-3 levels
ContextAny depthCallbacks needed by many unrelated components
State manager (Zustand)Any depthComplex shared actions across the app

What Interviewers Actually Check

  • Whether you know the callback prop pattern and can implement it correctly
  • Whether you type the callback prop in TypeScript
  • Whether you know that useCallback is needed to stabilize the reference for React.memo
  • Whether you know that inline arrow functions in JSX create new references on every render
  • Whether you know when to switch from callback props to context for deeply nested communication

Follow-Up Questions

  1. How would you call a child's imperative method from a parent using useImperativeHandle and forwardRef?
  2. If a callback is passed three levels deep (grandparent to grandchild), how does context solve the prop-drilling problem?
  3. When would you use an event bus or a global event emitter instead of callback props?
  4. How do you handle the case where a callback should only be callable once (a one-time confirmation dialog)?
  5. How does the callback pattern interact with React.memo: why does a new function reference break memoization?

Common Candidate Mistakes

  • Trying to access the parent state directly from the child (not possible in React's model)
  • Not using useCallback when the child is wrapped in React.memo, silently breaking memoization
  • Not typing the callback prop, losing type safety on the arguments the child passes back
  • Prop drilling callbacks through 4+ levels when context or a state manager would be cleaner
  • Defining the callback inline in JSX (onDelete={() => handleDelete(id)}) which creates a new reference on every render

Interview Readiness Checklist

Before you leave this question, make sure you can answer:

  • Can you pass a callback from a parent to a child and call it from the child?
  • Can you type a callback prop in TypeScript with the correct signature?
  • Can you explain why useCallback matters for stabilizing callback references for memoized children?
  • Can you explain the difference between passing data down (props) and passing events up (callbacks)?
  • Can you describe when to move from callback props to context for deeply nested component communication?

Summary

Child-to-parent communication in React is accomplished by passing callback functions from parent to child as props. The child calls the callback with any data it wants to pass back; the parent handles the call and updates its own state. The child never accesses or modifies parent state directly.

In TypeScript, callback props are typed as function signatures in the props interface, which enforces that the child passes the correct argument types. This is the most common pattern for localized component interactions: button clicks, form submissions, list item selections, and modal close events.

For memoized children (wrapped in React.memo), the callback reference must be stable. An inline arrow function in JSX creates a new reference on every parent render, causing the child to re-render regardless of React.memo. Wrapping the callback in useCallback with the correct dependency array stabilizes the reference. When callback props must travel through three or more component layers, context or a lightweight state manager is cleaner than prop drilling.

Related Videos
Watch these videos to learn more about this topic
Frequently Asked Questions

Can a child component directly modify parent state?

No. A child cannot access or modify the parent state directly. It can only call a callback function the parent passed as a prop, and the parent decides how to update its own state in response.

Advertisement


Stay Updated

Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.

Advertisement