# Child to Parent Communication in ReactJS: 6 Proven Patterns from the Source Code

> Master child to parent communication in ReactJS with 6 proven patterns from the React source code. Learn callback props, Context API, and useImperativeHandle to build better apps.

- Repository: [Meta/react](https://github.com/facebook/react)
- Tags: deep-dive
- Published: 2026-02-18

---

**The most effective methods for child-to-parent communication in ReactJS include callback props for simple events, the Context API for deep component trees, and `useImperativeHandle` with `forwardRef` for imperative actions, all implemented through core hooks defined in the facebook/react repository.**

React’s architecture enforces unidirectional data flow where props travel downward from parent to child. When a child component needs to communicate upward, developers must use specific patterns that preserve this immutable, declarative model while maintaining type safety and performance. The facebook/react repository provides several first-class mechanisms—implemented in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js) and related core files—that enable this communication without breaking the component encapsulation model.

## 1. Callback Props for Event-Driven Communication

The most fundamental pattern involves the parent passing a function to the child via props. The child invokes this function with data, effectively signaling upward intent without mutating parent state directly.

According to the source code in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js) (lines 35-41), parents should wrap these callbacks with **`useCallback`** to maintain reference equality across renders. Without this optimization, inline function declarations create new references on every render, causing unnecessary re-renders of memoized child components.

```jsx
// Parent.jsx
import {useState, useCallback} from 'react';
import Child from './Child';

export default function Parent() {
  const [message, setMessage] = useState('');

  const handleUpdate = useCallback(newMsg => {
    setMessage(newMsg);
  }, []);

  return (
    <>
      <Child onUpdate={handleUpdate} />
      <p>Message from child: {message}</p>
    </>
  );
}

```

```jsx
// Child.jsx
export default function Child({onUpdate}) {
  return (
    <button onClick={() => onUpdate('Hello from child!')}>
      Send to Parent
    </button>
  );
}

```

## 2. Lifting State Up to Common Ancestors

When multiple siblings need to share data or the parent must derive UI from child input, state should reside in the nearest common ancestor. Both the parent and child receive the current value via props, and the child updates it through callback props.

This pattern relies on **`useState`**, implemented in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js) (lines 1-22), which provides the setter function that eventually reaches the child as a callback. The parent controls *what* the child can do by exposing specific setter functions rather than the raw state.

## 3. Imperative Handles with forwardRef and useImperativeHandle

For cases where the parent must trigger functions not naturally expressed as data flow—such as focusing an input, scrolling to a position, or playing media—React provides the **`forwardRef`** API coupled with **`useImperativeHandle`**.

The `forwardRef` implementation in [`packages/react/src/ReactForwardRef.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactForwardRef.js) (lines 12-55) validates the render function at development time (lines 18-30), ensuring the component accepts the ref argument correctly. Meanwhile, `useImperativeHandle` in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js) (lines 51-57) warns if the ref argument is not an object or function, providing runtime safety.

```jsx
// FancyInput.jsx
import React, {forwardRef, useImperativeHandle, useRef} from 'react';

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  // expose a `focus` method to the parent
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current && inputRef.current.focus(),
    clear: () => (inputRef.current.value = ''),
  }));

  return <input ref={inputRef} {...props} />;
});

export default FancyInput;

```

```jsx
// Parent.jsx
import React, {useRef} from 'react';
import FancyInput from './FancyInput';

export default function Parent() {
  const fancyRef = useRef();

  return (
    <>
      <FancyInput ref={fancyRef} placeholder="Click button to focus" />
      <button onClick={() => fancyRef.current.focus()}>
        Focus Input
      </button>
      <button onClick={() => fancyRef.current.clear()}>
        Clear Input
      </button>
    </>
  );
}

```

## 4. Context API for Deep Component Trees

Passing callbacks through multiple layers of components creates prop drilling. The **Context API**, defined in [`packages/react/src/ReactContext.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactContext.js) (lines 14-23), creates a store that any descendant can access via `useContext`.

The parent supplies the value through a Provider, exposing both data and setter functions to deeply nested children without intermediate components forwarding props. This turns the parent into a *provider* of shared data while maintaining React's declarative nature.

```jsx
// MessageContext.js
import React, {createContext, useState} from 'react';

export const MessageContext = createContext({
  message: '',
  setMessage: () => {},
});

export function MessageProvider({children}) {
  const [message, setMessage] = useState('');
  return (
    <MessageContext.Provider value={{message, setMessage}}>
      {children}
    </MessageContext.Provider>
  );
}

```

```jsx
// DeepChild.jsx
import React, {useContext} from 'react';
import {MessageContext} from './MessageContext';

export default function DeepChild() {
  const {setMessage} = useContext(MessageContext);
  return (
    <button onClick={() => setMessage('Sent from deep child')}>
      Update Parent (via Context)
    </button>
  );
}

```

```jsx
// App.jsx
import {MessageProvider} from './MessageContext';
import DeepChild from './DeepChild';
import {useContext} from 'react';
import {MessageContext} from './MessageContext';

function Display() {
  const {message} = useContext(MessageContext);
  return <p>Current message: {message}</p>;
}

export default function App() {
  return (
    <MessageProvider>
      <Display />
      <DeepChild />
    </MessageProvider>
  );
}

```

## 5. useReducer and Dispatch Functions

For complex state transitions, the parent creates a reducer and passes the **`dispatch`** function to children. Children communicate by dispatching actions (`{type: 'INCREMENT', payload}`) rather than calling callbacks directly, keeping the data flow explicit and testable.

The `useReducer` hook shares the same core implementation as `useState` in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js) (lines 1-22), ensuring consistent scheduling and batching of updates.

```jsx
// Parent.jsx
import React, {useReducer} from 'react';
import Child from './Child';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {...state, count: state.count + 1};
    default:
      return state;
  }
}

export default function Parent() {
  const [state, dispatch] = useReducer(reducer, {count: 0});

  return (
    <>
      <Child dispatch={dispatch} />
      <p>Count: {state.count}</p>
    </>
  );
}

```

```jsx
// Child.jsx
export default function Child({dispatch}) {
  return (
    <button onClick={() => dispatch({type: 'increment'})}>
      Increment in Parent
    </button>
  );
}

```

## 6. useSubscription for External Stores

When integrating with third-party state containers or observables outside React's tree, the **`useSubscription`** hook provides a standardized way to subscribe to external data sources and receive updates. This pattern is implemented in [`packages/use-subscription/src/useSubscription.js`](https://github.com/facebook/react/blob/main/packages/use-subscription/src/useSubscription.js) and is commonly used by state-container libraries like Redux to notify React components of external changes.

## Performance and Safety Considerations

To optimize child-to-parent communication according to the React source architecture:

- **Wrap callbacks with `useCallback`** and derived data with `useMemo` to preserve reference equality. The implementation in [`ReactHooks.js`](https://github.com/facebook/react/blob/main/ReactHooks.js) ensures these hooks maintain stable references across renders when dependency arrays match.
- **Memoize imperative handles**. When using `useImperativeHandle`, memoize the returned object to avoid creating new method instances each render.
- **Keep Context values granular**. Avoid placing large mutable objects in the Provider value to prevent widespread re-renders of all consumers.

Type-checking is enforced at the component boundary. `forwardRef` validates render functions during development (as seen in [`ReactForwardRef.js`](https://github.com/facebook/react/blob/main/ReactForwardRef.js) lines 18-30), and `useImperativeHandle` validates ref argument types (lines 51-55 of [`ReactHooks.js`](https://github.com/facebook/react/blob/main/ReactHooks.js)), providing development-time warnings before runtime errors occur.

Real-world applications often combine these patterns. For example, a form component might lift state up to share data with siblings while also exposing an imperative handle via `useImperativeHandle` to allow the parent to reset the form programmatically.

## Summary

- **Callback props** are the standard pattern for event-based communication, optimized with `useCallback` from [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js)
- **Lifting state up** centralizes shared data in common ancestors using `useState`
- **`useImperativeHandle`** with **`forwardRef`** exposes imperative APIs for focus and control, with validation in [`packages/react/src/ReactForwardRef.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactForwardRef.js)
- **Context API** eliminates prop drilling through `createContext` in [`packages/react/src/ReactContext.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactContext.js)
- **`useReducer`** dispatches actions for complex logic while maintaining immutable updates
- **`useSubscription`** integrates external stores via [`packages/use-subscription/src/useSubscription.js`](https://github.com/facebook/react/blob/main/packages/use-subscription/src/useSubscription.js)

## Frequently Asked Questions

### What is the difference between callback props and useImperativeHandle?

**Callback props** facilitate declarative communication where the child notifies the parent of events or data changes through function invocation. **`useImperativeHandle`** provides an imperative escape hatch where the parent directly invokes methods on the child, useful for focus management, media controls, or animations that don't fit the declarative data-flow model.

### When should I use Context API instead of callback props?

Use **Context API** when components are separated by multiple intermediate layers, making prop drilling cumbersome and error-prone. According to the [`ReactContext.js`](https://github.com/facebook/react/blob/main/ReactContext.js) implementation, Context is most efficient for truly global or deeply shared state. For direct parent-child relationships, callback props remain preferable to maintain component encapsulation and explicit data contracts.

### How does useCallback prevent performance issues in child-to-parent communication?

According to the implementation in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js) (lines 35-41), **`useCallback`** memoizes the function reference across renders. Without it, inline arrow functions create new references on every parent render, causing React to re-render pure children unnecessarily. This preservation of reference equality ensures that `React.memo` or `PureComponent` optimizations in children remain effective.

### Is it safe to pass the dispatch function from useReducer to deep child components?

**Yes.** The `dispatch` function from **`useReducer`** maintains a stable reference across renders and does not cause re-renders of consuming components unless they also subscribe to the state. This makes it safe to pass through Context or deep prop chains without memoization concerns, unlike typical callback functions that require `useCallback` wrapping.