In the previous article, we created our first Saludo component. It worked, but it had a serious design flaw: it was static.
That is, it always greeted “Luis”. But if we wanted to greet “María”, we would have to create a different component (And… that wouldn’t be very practical).
In JavaScript (and almost any language), when we want a function to be dynamic, we pass it arguments.
The equivalent in React Components is that when we want a component to be dynamic, we pass it Props (short for Properties).
Props are the fundamental mechanism React has to pass data from a parent component to a child component.
Passing Data (The Parent)
Imagine we are in App.jsx (the parent) and we want to use our Saludo component (the child) several times, but with different names.
That is, basically what we would like is to do this, using a syntax identical to the HTML attributes you already know:
// src/App.jsx
import Saludo from './components/Saludo';
function App() {
return (
<div>
{/* We pass the prop "nombre" with a string value */}
<Saludo nombre="Luis" />
<Saludo nombre="María" />
<Saludo nombre="Carlos" />
</div>
)
}
At this moment, React takes all those attributes we wrote (nombre="Luis") and packs them into a single object.
Receiving Data (The Child)
Now let’s go to the Saludo.jsx component. React takes that object it created with our attributes, and injects it as the first argument of the component’s function.
Simply by convention, we call this argument props.
// src/components/Saludo.jsx
// Option 1: Receiving the entire props object
function Saludo(props) {
// props is { nombre: "Luis" }
return <h2>Hola, {props.nombre}!</h2>;
}
export default Saludo;
We have just made our component reusable. The component no longer “knows” what name it will display, it simply displays what it is sent.
Destructuring Props
Although props.nombre works, in the real world you will rarely see code written like that. It’s verbose and repetitive to write props. all the time.
Instead, we generally use JavaScript’s Destructuring of objects directly in the function signature.
// Option 2: Destructuring (Recommended)
function Saludo({ nombre }) {
// We extract "nombre" directly
return <h2>Hola, {nombre}!</h2>;
}
This is much cleaner. Furthermore, it allows us to see at a glance what data the component needs to function without having to read all the code looking for props.something.
Passing Other Types of Data
So far we have passed a string literal ("Luis"). But props can be anything: numbers, booleans, arrays, objects, functions, and even other JSX components.
But if the value is not a string literal, we must use curly braces { }.
<Producto
titulo="Laptop Gaming" // String
precio={1200} // Number
enStock={true} // Boolean
tags={['asus', 'tech']} // Array
detalles={{ peso: '2kg' }} // Object
/>
In the child component, we would receive it in the same way:
function Producto({ titulo, precio, enStock, tags, detalles }) {
return (
<div className="card">
<h3>{titulo}</h3>
<p>Precio: {precio}€</p>
<p>{enStock ? 'Disponible' : 'Agotado'}</p>
<small>Peso: {detalles.peso}</small>
</div>
);
}
Notice the boolean enStock. React does not render true or false on screen by default. That’s why we use a ternary to display text.
Default Props
What happens if we use the <Saludo /> component without passing it the name?
// App.jsx
<Saludo /> // We don't pass "nombre"
Inside the component, nombre will be undefined. The result will be “Hola, !” or an error if we try to manipulate that variable.
To avoid this, we can define default values.
// src/components/Saludo.jsx
function Saludo({ nombre = "Invitado" }) {
return <h2>Hola, {nombre}!</h2>;
}
Now, if we don’t pass anything, nombre will automatically be “Invitado”. More robust against failures.
In the past, a property called defaultProps was used, but nowadays we use the native ES6 default parameters syntax:
Props are immutable
Props are read-only. A component should NEVER modify its own props.
function Saludo({ nombre }) {
nombre = "Pepe"; // ¡ERROR! Don't modify what you receive
return <h1>{nombre}</h1>
}
React follows a Unidirectional data flow (One-Way Data Flow). Data flows downwards, from parent to child. The child receives data and displays it.
If the child needs to change something, it doesn’t change the prop; it must ask the parent to change the value and send it again.
This restriction is one of the characteristics that makes React applications predictable and easy to debug (and a nightmare sometimes too).
