📚 Render Props en React
🧠 ¿Qué es Render Props?
Render Props es un patrón en React donde un componente recibe una función como prop y esa función devuelve JSX que el componente renderiza.
La idea principal es separar la lógica de la presentación:
- Un componente maneja datos o lógica
- Otro componente decide cómo se muestran esos datos
En forma simple:
Componente A → provee datos o lógica
Componente B → decide cómo renderizar la UI
La prop normalmente se llama:
render
children
component
pero lo más común es render.
🧩 Ejemplo 1: Render Props básico
Código
interface ChildComponentProps {
render: (name: string) => JSX.Element;
}
export const ChildComponent = ({ render }: ChildComponentProps) => {
const name = "harold";
return <div>{render(name)}</div>;
};
export const ParentComponent = () => {
return <ChildComponent render={(name) => <p>Hello, {name}</p>} />;
};
🔎 Qué está pasando
1️⃣ ChildComponent
Este componente no decide qué renderizar.
Solo tiene un dato (name) y ejecuta la función que recibe.
const name = "harold";
return <div>{render(name)}</div>;
Aquí ocurre algo importante:
render(name)
Esto ejecuta la función que el padre le pasó.
2️⃣ ParentComponent
El componente padre pasa una función como prop:
render={(name) => <p>Hello, {name}</p>}
Esto significa:
Cuando tengas el name
→ usa esta función para renderizarlo
🔄 Flujo del ejemplo
ParentComponent
│
│ pasa función render
▼
ChildComponent
│
│ tiene el dato: "harold"
▼
render("harold")
│
▼
<p>Hello, harold</p>
│
▼
<div><p>Hello, harold</p></div>
🧩 Ejemplo 2: Render Props con estado
Este ejemplo es más realista porque un componente maneja el estado y otro renderiza la UI.
📦 ParentComponent
import React, { useState } from "react";
interface ParentComponentProps {
render: (data: string[]) => React.ReactNode;
}
const ParentComponent = ({ render }: ParentComponentProps) => {
const [data] = useState<string[]>(["apple", "banana", "Orange"]);
return <ul>{render(data)}</ul>
};
export default ParentComponent;
Este componente:
- Tiene estado (
data)
- No decide cómo mostrarlo
- Solo ejecuta
render(data)
Su responsabilidad es proveer los datos.
🍎 ChildComponent
interface ChildComponentProps {
data: string[];
}
const ChildComponent = ({ data }: ChildComponentProps) => {
return data.map((item, index) => <li key={index}>{item}</li>);
};
export default ChildComponent;
Este componente solo se encarga de la UI.
Transforma:
["apple","banana","Orange"]
en:
<li>apple</li>
<li>banana</li>
<li>Orange</li>
🧩 Uso del patrón
import ChildComponent from "./ChildComponent";
import ParentComponent from "./ParentComponent";
export const RenderProps2 = () => {
return (
<div>
<ParentComponent
render={(data: string[]) => <ChildComponent data={data} />}
/>
</div>
);
};
Aquí sucede lo siguiente:
1️⃣ ParentComponent tiene los datos
2️⃣ Llama a:
render(data)
3️⃣ La función devuelve:
<ChildComponent data={data} />
4️⃣ ChildComponent renderiza la lista.
🔄 Flujo completo
RenderProps2
│
▼
ParentComponent
(tiene el estado)
["apple","banana","Orange"]
│
▼
render(data)
│
▼
ChildComponent
│
▼
<li>apple</li>
<li>banana</li>
<li>Orange</li>
│
▼
<ul>
<li>apple</li>
<li>banana</li>
<li>Orange</li>
</ul>
🎯 ¿Para qué sirve Render Props?
Este patrón permite compartir lógica entre componentes sin acoplar la UI.
Es útil para lógica como:
- manejo de estado
- fetch de datos
- tracking del mouse
- autenticación
- control de formularios
- scroll tracking
La idea es que un componente provee la lógica y los datos, mientras que otro decide cómo mostrarlos.
✅ Ventajas
🔹 Reutilización de lógica
Un mismo componente puede compartir su lógica con diferentes interfaces.
Ejemplo:
<DataFetcher render={...}/>
<DataFetcher render={...}/>
<DataFetcher render={...}/>
Cada uno puede renderizar los datos de forma diferente.
🔹 Separación de responsabilidades
Lógica → Parent
UI → Child
Esto hace el código más organizado y fácil de mantener.
🔹 Muy flexible
Permite controlar completamente cómo se renderiza la UI.
🔹 Muy usado en librerías
Muchas librerías populares utilizaron este patrón:
- Formik
- Downshift
- React Motion
❌ Desventajas
🔸 Código más difícil de leer
Cuando se usan muchos render props pueden aparecer funciones anidadas:
<Component
render={(data) => (
<AnotherComponent
render={(value) => (
<Something data={data} value={value}/>
)}
/>
)}
/>
Esto se conoce como:
😵 Callback Hell o Render Hell.
🔸 Más re-renderizados
Cada render crea una nueva función:
render={(data) => ...}
Esto puede causar renders innecesarios si no se optimiza con:
🔸 Curva de aprendizaje 📈
El patrón puede ser difícil de entender al principio, especialmente para desarrolladores que recién empiezan con React.
Puede resultar confuso:
- pasar funciones como props
- entender cuándo se ejecutan
- entender quién controla el render
🔸 Puede generar Prop Drilling 📦
A veces los datos deben pasar por varios componentes o funciones antes de llegar al que los necesita.
Ejemplo:
<DataProvider
render={(data) => (
<Layout
render={() => (
<List data={data} />
)}
/>
)}
/>
Aquí data pasa por varios niveles antes de llegar al componente final.
🧠 Nota importante
El patrón Render Props fue muy popular antes de React Hooks (2019).
Hoy muchos casos se resuelven mejor con Custom Hooks, que hacen el código más simple.
Ejemplo moderno:
const data = useData();
en lugar de:
<DataProvider render={(data) => ... } />