Chapter 1: Advanced React Patterns
Chapter 1 of 15
Chapter 1: Advanced React Patterns
1.1 Render Props Pattern
Render props is a pattern where a component receives a function as a prop that returns React elements. This allows sharing code between components.
// Render prop component
function DataProvider({ render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchData().then(result => {
setData(result);
setLoading(false);
});
}, []);
return render({ data, loading });
}
// Usage
<DataProvider render={({ data, loading }) => (
loading ? <div>Loading...</div> : <div>{data}</div>
)} />
// Alternative: children as function
function DataProvider({ children }) {
const [data, setData] = useState(null);
// ...
return children({ data, loading });
}
<DataProvider>
{({ data, loading }) => (
loading ? <div>Loading...</div> : <div>{data}</div>
)}
</DataProvider>
1.2 Higher-Order Components (HOCs)
HOCs are functions that take a component and return a new component with additional functionality.
// HOC for authentication
function withAuth(WrappedComponent) {
return function AuthenticatedComponent(props) {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
checkAuth().then(auth => {
setIsAuthenticated(auth);
setLoading(false);
});
}, []);
if (loading) return <div>Loading...</div>;
if (!isAuthenticated) return <div>Please log in</div>;
return <WrappedComponent {...props} />;
};
}
// Usage
const ProtectedProfile = withAuth(Profile);
// HOC for data fetching
function withData(WrappedComponent, fetchFunction) {
return function DataComponent(props) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchFunction(props).then(result => {
setData(result);
setLoading(false);
});
}, [props]);
return <WrappedComponent {...props} data={data} loading={loading} />;
};
}
1.3 Compound Components
// Components that work together
function Tabs({ children }) {
const [activeTab, setActiveTab] = useState(0);
return (
<div className="tabs">
{React.Children.map(children, (child, index) =>
React.cloneElement(child, {
isActive: index === activeTab,
onClick: () => setActiveTab(index)
})
)}
</div>
);
}
function Tab({ isActive, onClick, children }) {
return (
<button
className={isActive ? 'active' : ""}
onClick={onClick}
>
{children}
</button>
);
}
// Usage
<Tabs>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</Tabs>