Приведи пример написания кастомного HOC
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Пример написания кастомного HOC
Higher-Order Component (HOC) - это продвинутый паттерн для переиспользования логики компонентов в React. HOC - это функция, которая принимает компонент и возвращает новый компонент с дополнительной функциональностью. Это один из самых мощных инструментов для организации кода в React-приложениях.
Что такое HOC и зачем он нужен?
HOC позволяет:
- Переиспользовать логику между несколькими компонентами
- Манипулировать props - добавлять, удалять или трансформировать их
- Перехватывать методы жизненного цикла (в классовых компонентах)
- Управлять состоянием для оборачиваемого компонента
- Рендерить элементы условно в зависимости от логики
Практический пример: HOC для аутентификации
Вот реальный пример HOC, который проверяет аутентификацию пользователя и перенаправляет на страницу входа, если он не авторизован:
// withAuth.js - HOC для защиты маршрутов
function withAuth(WrappedComponent) {
return function AuthComponent(props) {
const [isLoading, setIsLoading] = React.useState(true);
const [isAuthenticated, setIsAuthenticated] = React.useState(false);
React.useEffect(() => {
// Проверяем аутентификацию при монтировании
const checkAuth = async () => {
try {
const response = await fetch('/api/me');
if (response.ok) {
setIsAuthenticated(true);
} else {
window.location.href = '/login';
}
} catch (error) {
window.location.href = '/login';
} finally {
setIsLoading(false);
}
};
checkAuth();
}, []);
if (isLoading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return null;
}
// Передаём все props в оборачиваемый компонент
return <WrappedComponent {...props} />;
};
}
// Использование
function Dashboard() {
return <h1>Welcome to Dashboard</h1>;
}
export default withAuth(Dashboard);
Пример 2: HOC для управления темой
HOC, который добавляет функциональность переключения между светлой и тёмной темой:
// withTheme.js
function withTheme(WrappedComponent) {
return function ThemedComponent(props) {
const [theme, setTheme] = React.useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
// Добавляем props для темы в оборачиваемый компонент
return (
<div className={`theme-${theme}`}>
<WrappedComponent
{...props}
theme={theme}
toggleTheme={toggleTheme}
/>
</div>
);
};
}
// Использование
function Settings({ theme, toggleTheme }) {
return (
<div>
<p>Current theme: {theme}</p>
<button onClick={toggleTheme}>Switch Theme</button>
</div>
);
}
export default withTheme(Settings);
Пример 3: HOC для подписки на данные
HOC, который подписывает компонент на обновления данных:
// withDataFetching.js
function withDataFetching(url) {
return function WrappingComponent(WrappedComponent) {
return function DataFetchingComponent(props) {
const [data, setData] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(null);
React.useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to fetch');
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
return (
<WrappedComponent
{...props}
data={data}
loading={loading}
error={error}
/>
);
};
};
}
// Использование
function UsersList({ data, loading, error }) {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default withDataFetching('/api/users')(UsersList);
Практические советы по использованию HOC
1. Копируй статические методы
При оборачивании компонента теряются его статические методы. Используй библиотеку hoist-non-react-statics:
import hoistNonReactStatics from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
class Enhance extends React.Component { /*...*/ }
hoistNonReactStatics(Enhance, WrappedComponent);
return Enhance;
}
2. Правильно называй оборачиваемый компонент
function enhance(WrappedComponent) {
class Enhance extends React.Component { /*...*/ }
Enhance.displayName = `Enhance(${getDisplayName(WrappedComponent)})`;
return Enhance;
}
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
3. Композиция HOC
Можно комбинировать несколько HOC:
const EnhancedComponent = withTheme(withAuth(Dashboard));
Альтернатива: Custom Hooks
В современном React HOC часто заменяют кастомными хуками, которые проще для понимания:
// useAuth.js - кастомный хук вместо HOC
function useAuth() {
const [isAuthenticated, setIsAuthenticated] = React.useState(false);
React.useEffect(() => {
fetch('/api/me')
.then(res => {
if (res.ok) setIsAuthenticated(true);
});
}, []);
return isAuthenticated;
}
// Использование в компоненте
function Dashboard() {
const isAuth = useAuth();
if (!isAuth) return <Redirect to="/login" />;
return <h1>Dashboard</h1>;
}
HOC - мощный инструмент, но для функциональных компонентов рекомендуется использовать кастомные хуки, так как они более интуитивны и легче отлаживаются.