Advanced Render Props
Learn the Render Props pattern to share code between components using a function as a child pattern. This is a foundational concept in component-based UI development that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world React experience. Take your time with each section and practice the examples
Understanding Render Props
Render Props is a technique for sharing code between React components using a prop whose value is a function. This pattern allows components to share logic while maintaining flexibility.. This is an essential concept that every React developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
Basic Render Props Implementation
// Basic render props example
const Mouse = ({ render }) => {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (event) => {
setPosition({
x: event.clientX,
y: event.clientY
});
};
return (
<div onMouseMove={handleMouseMove}>
{render(position)}
</div>
);
};
// Usage
<Mouse render={({ x, y }) => (
<div>
Mouse position: {x}, {y}
</div>
)} />Advanced Render Props Patterns
// Data fetching with render props
const DataFetcher = ({ url, children }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(error => {
setError(error);
setLoading(false);
});
}, [url]);
return children({ data, loading, error });
};
// Usage
<DataFetcher url="/api/users">
{({ data, loading, error }) => {
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <UserList users={data} />;
}}
</DataFetcher>Render Props vs HOCs
- Render Props: More flexible, easier to test, explicit dependencies
- HOCs: More declarative, can be composed, less verbose
- Use Render Props for: Complex state logic, dynamic behavior
- Use HOCs for: Cross-cutting concerns, simple enhancements
- Both patterns can be used together effectively — a critical concept in component-based UI development that you will use frequently in real projects
Common Render Props Examples
// Toggle render prop
const Toggle = ({ children }) => {
const [isOn, setIsOn] = useState(false);
const toggle = () => setIsOn(!isOn);
return children({ isOn, toggle });
};
// Form render prop
const Form = ({ children, onSubmit }) => {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const handleChange = (name, value) => {
setValues(prev => ({ ...prev, [name]: value }));
setErrors(prev => ({ ...prev, [name]: '' }));
};
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(values);
};
return children({
values,
errors,
handleChange,
handleSubmit
});
};Mini-Project: Modal Render Prop
// Complete modal render prop implementation
import React, { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
const Modal = ({ isOpen, onClose, children }) => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'unset';
}
return () => {
document.body.style.overflow = 'unset';
};
}, [isOpen]);
if (!mounted || !isOpen) return null;
return createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
{children}
</div>
</div>,
document.body
);
};
const ModalProvider = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const [modalContent, setModalContent] = useState(null);
const openModal = (content) => {
setModalContent(content);
setIsOpen(true);
};
const closeModal = () => {
setIsOpen(false);
setModalContent(null);
};
return (
<>
{children({ openModal, closeModal })}
<Modal isOpen={isOpen} onClose={closeModal}>
{modalContent}
</Modal>
</>
);
};
// Usage
<ModalProvider>
{({ openModal, closeModal }) => (
<div>
<button onClick={() => openModal(<div>Modal Content</div>)}>
Open Modal
</button>
</div>
)}
</ModalProvider>