React Essentials — Components, JSX, Props, State & MoreEntry 3 of 11

Passing Functions as Props (Child-to-Parent Communication)

A parent passes a function to a child; the child calls it to notify the parent that something happened.

Notes

Because data flows down only, a child can't change parent state directly. Instead, the parent passes a callback (by convention named onSomething). The child calls it, typically in response to a user event, and the parent's logic runs.

Pass the function reference (onClick={onSelect}), not a call (onClick={onSelect()}). The second form runs immediately during render.

What / Why / When

What

Callbacks as props.

Why

State stays in the parent while the child stays reusable and unaware of the parent's logic.

When

A child's user interaction must update state owned higher up.

Use case

Tab buttons that tell the parent which tab is selected.

Code compare

React
import { useState, type ReactNode } from "react";​type TabButtonProps = {  children: ReactNode;  isSelected: boolean;  onSelect: () => void;};​export function TabButton({ children, isSelected, onSelect }: TabButtonProps) {  return (    <button className={isSelected ? "active" : undefined} onClick={onSelect}>      {children}    </button>  );}​type Topic = "components" | "props" | "state";​export function Examples() {  const [selectedTopic, setSelectedTopic] = useState<Topic>("components");​  return (    <menu>      <TabButton        isSelected={selectedTopic === "components"}        onSelect={() => setSelectedTopic("components")}      >        Components      </TabButton>    </menu>  );}
Angular
import { Component, input, output } from "@angular/core";​@Component({  selector: "app-tab-button",  template: `    <button [class.active]="isSelected()" (click)="selected.emit()">      <ng-content />    </button>  `,})export class TabButtonComponent {  isSelected = input(false);  selected = output<void>();}
Angular
<!-- Parent template; selectedTopic = signal<Topic>('components') --><app-tab-button  [isSelected]="selectedTopic() === 'components'"  (selected)="selectedTopic.set('components')">  Components</app-tab-button>

Differences and gotchas

  • output() (v17.3+) replaces @Output() with EventEmitter. The decorator style still works but isn't the modern default.
  • React passes a function down. Angular emits an event up. Same result, inverted mechanism.
  • Angular's style guide discourages the on prefix for outputs: (selected), not (onSelected).

Quick reference

ConceptReactAngular
Declare in childonSelect: () => void propselected = output<void>()
TriggeronSelect()this.selected.emit()
Listen in parentonSelect={handleSelect}(selected)="handleSelect()"