← React & TypeScript

React Fundamentals

What is a component in React and what are props?

Show answer — try answering out loud first

A component is a function that receives props and returns JSX: a reusable piece of UI. Props are the data the parent component passes to the child; they are read-only and always flow in one direction, from parent to child.

Think of a component as a LEGO brick: you define it once and use it many times with different data. A React app is a tree of components composed inside one another. Props (from properties) are the mechanism for passing information to a component:

  • They're like a function's arguments: the parent "calls" the child with certain values.
  • They're read-only: a component should never modify its props. If it needs data that changes, that's state, not props.
  • Unidirectional flow: data flows down from parent to child. If a child needs to "notify" something upward, the parent passes it a function via props and the child calls it.
  • Composition: instead of inheritance, React favors composing small components. The special children prop lets a component wrap others.
  • With TypeScript: typing the props with a type documents the component's contract and prevents errors when using it.
import type { ReactNode } from "react";

// Props are typed with a type: this is the component's "contract"
type SaludoProps = { nombre: string; edad?: number };

function Saludo({ nombre, edad }: SaludoProps) {
  return <p>Hola, {nombre}{edad !== undefined && ` (${edad} años)`}</p>;
}

// Composition: Tarjeta doesn't know what it contains, it just wraps its children
type TarjetaProps = { titulo: string; children: ReactNode };

function Tarjeta({ titulo, children }: TarjetaProps) {
  return (
    <section className="tarjeta">
      <h2>{titulo}</h2>
      {children}
    </section>
  );
}

function App() {
  return (
    <Tarjeta titulo="Equipo">
      {/* The same component, reused with different props */}
      <Saludo nombre="Ana" edad={28} />
      <Saludo nombre="Luis" />
    </Tarjeta>
  );
}

// Expected output:
// Equipo / Hola, Ana (28 años) / Hola, Luis

Trying to mutate props inside the child:

// BAD: reassigning the prop doesn't re-render and breaks the contract
function Contador({ contador }: { contador: number }) {
  return <button onClick={() => (contador = contador + 1)}>{contador}</button>;
}

// GOOD: the child notifies with a function; the parent (data owner) updates
type Props = { contador: number; onIncrementar: () => void };
function ContadorBien({ contador, onIncrementar }: Props) {
  return <button onClick={onIncrementar}>{contador}</button>;
}

"A component is a function that receives props and returns JSX; it's the basic unit of reuse in React. Props are the data the parent passes to the child, like a function's arguments, and they're read-only: a component should never modify them. Data flows in a single direction, from parent to child, and when the child needs to communicate something upward, it receives a function via props and runs it. Instead of inheritance, React uses composition: I build complex interfaces by combining small components, often with the children prop."

Quick challenge

Create a Boton component that receives via props a texto: string, an optional prop deshabilitado?: boolean, and a function onPresionar: () => void. Use it from an App component that shows two buttons: one active and one disabled.

See solution
type BotonProps = {
  texto: string;
  deshabilitado?: boolean;
  onPresionar: () => void;
};

function Boton({ texto, deshabilitado = false, onPresionar }: BotonProps) {
  return (
    <button disabled={deshabilitado} onClick={onPresionar}>
      {texto}
    </button>
  );
}

function App() {
  return (
    <>
      <Boton texto="Guardar" onPresionar={() => console.log("guardado")} />
      <Boton texto="Eliminar" deshabilitado onPresionar={() => {}} />
    </>
  );
}