Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Higher-Order Component (HOC) в React
HOC (компонент высшего порядка) — это продвинутый паттерн в React для переиспользования логики компонентов. HOC — это функция, которая берёт компонент и возвращает новый компонент с дополнительной функциональностью. Это паттерн композиции, а не наследования.
Основная концепция
// HOC — это функция
const EnhancedComponent = higherOrderComponent(OriginalComponent);
// Где higherOrderComponent выглядит так:
function higherOrderComponent(Component) {
return function EnhancedComponent(props) {
// Добавляем функциональность
return <Component {...props} />;
};
}
Пример 1: HOC для логирования
// HOC который логирует пропсы
function withLogger(Component) {
return function EnhancedComponent(props) {
useEffect(() => {
console.log('Component mounted:', Component.name);
console.log('Props:', props);
return () => {
console.log('Component unmounted:', Component.name);
};
}, [props]);
return <Component {...props} />;
};
}
// Использование
function MyComponent({ name }) {
return <div>Hello, {name}!</div>;
}
const LoggedComponent = withLogger(MyComponent);
// Теперь при использовании LoggedComponent все пропсы будут логироваться
<LoggedComponent name="Alice" />
Пример 2: HOC для подключения к Redux
// Классический паттерн — подключение компонента к Redux
function withRedux(Component) {
return function EnhancedComponent(props) {
const dispatch = useDispatch();
const data = useSelector(state => state.data);
return (
<Component
{...props}
data={data}
dispatch={dispatch}
/>
);
};
}
// Использование
function UserList({ data, dispatch }) {
return (
<div>
{data.users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
const ConnectedUserList = withRedux(UserList);
Пример 3: HOC для защиты маршрутов (Authentication)
// HOC для проверки аутентификации
function withAuth(Component) {
return function EnhancedComponent(props) {
const { isAuthenticated, loading } = useAuth();
if (loading) return <div>Загрузка...</div>;
if (!isAuthenticated) {
return <Navigate to="/login" />;
}
return <Component {...props} />;
};
}
// Использование
function Dashboard() {
return <div>Защищённая страница</div>;
}
const ProtectedDashboard = withAuth(Dashboard);
// Теперь ProtectedDashboard доступна только для аутентифицированных пользователей
Пример 4: HOC для управления темой
function withTheme(Component) {
return function EnhancedComponent(props) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<div className={`theme-${theme}`}>
<Component
{...props}
theme={theme}
toggleTheme={toggleTheme}
/>
</div>
);
};
}
// Использование
function App({ theme, toggleTheme }) {
return (
<div>
<p>Текущая тема: {theme}</p>
<button onClick={toggleTheme}>Переключить тему</button>
</div>
);
}
const ThemedApp = withTheme(App);
Пример 5: HOC для данных (Data Fetching)
// HOC который подгружает данные
function withDataFetching(url) {
return function(Component) {
return function EnhancedComponent(props) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
return (
<Component
{...props}
data={data}
loading={loading}
error={error}
/>
);
};
};
}
// Использование
function PostList({ data, loading, error }) {
if (loading) return <div>Загрузка...</div>;
if (error) return <div>Ошибка: {error}</div>;
return (
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
const PostListWithData = withDataFetching('/api/posts')(PostList);
Пример 6: HOC для валидации пропсов
// HOC который валидирует пропсы
function withPropValidation(Component, requiredProps) {
return function EnhancedComponent(props) {
const missingProps = requiredProps.filter(prop => !(prop in props));
if (missingProps.length > 0) {
return (
<div style={{ color: 'red' }}>
Ошибка: отсутствуют пропсы: {missingProps.join(', ')}
</div>
);
}
return <Component {...props} />;
};
}
// Использование
function UserProfile({ userId, userName }) {
return <div>{userName} (ID: {userId})</div>;
}
const ValidatedUserProfile = withPropValidation(
UserProfile,
['userId', 'userName']
);
// Если не передать userId или userName — будет ошибка
<ValidatedUserProfile userId={1} /> // Ошибка: отсутствуют пропсы: userName
Композиция HOC
Можно применять несколько HOC'ов к одному компоненту:
// Применение нескольких HOC'ов
const EnhancedComponent = withTheme(
withLogger(
withAuth(MyComponent)
)
);
// Или с помощью утилиты compose
const compose = (...hocs) => (component) =>
hocs.reduceRight((comp, hoc) => hoc(comp), component);
const EnhancedComponent = compose(
withTheme,
withLogger,
withAuth
)(MyComponent);
// Порядок важен! Внутренние HOC'ы выполняются первыми
Правильные имена
// ✅ Хорошо: имя отражает, что HOC делает
function withSubscription(Component) { ... }
function withDataFetching(url) { ... }
function withTheme(Component) { ... }
// ❌ Плохо: непонятное имя
function enhance(Component) { ... }
function wrapper(Component) { ... }
Передача статических методов
// ⚠️ Проблема: статические методы HOC'а не копируются
function MyComponent() { ... }
MyComponent.staticMethod = () => 'Hello';
const Enhanced = withSomeHOC(MyComponent);
Enhanced.staticMethod(); // undefined!
// ✅ Решение: используй hoist-non-react-statics
import hoistNonReactStatics from 'hoist-non-react-statics';
function withSomeHOC(Component) {
function EnhancedComponent(props) {
return <Component {...props} />;
}
hoistNonReactStatics(EnhancedComponent, Component);
return EnhancedComponent;
}
Refs и HOC
// ⚠️ Проблема: refs не пробрасываются
const EnhancedComponent = withSomeHOC(MyComponent);
const ref = React.createRef();
<EnhancedComponent ref={ref} />; // ref не пройдёт в MyComponent
// ✅ Решение: используй React.forwardRef
function withSomeHOC(Component) {
const EnhancedComponent = React.forwardRef((props, ref) => {
return <Component {...props} forwardedRef={ref} />;
});
EnhancedComponent.displayName = `WithHOC(${Component.displayName})`;
return EnhancedComponent;
}
Лучшие практики
- Не используй HOC в render методе — это создаёт новый компонент при каждом рендере
// ❌ Плохо
function App() {
const Enhanced = withSomeHOC(MyComponent); // новый компонент каждый раз
return <Enhanced />;
}
// ✅ Хорошо
const Enhanced = withSomeHOC(MyComponent);
function App() {
return <Enhanced />;
}
- Копируй статические методы через hoist-non-react-statics
- Пробрасывай refs через forwardRef
- Давай понятные имена компонентам
- Документируй какие пропсы добавляет HOC
HOC vs Hooks
Сейчас HOC часто заменяют Custom Hooks, которые проще и понятнее:
// Вместо HOC
const ConnectedComponent = withRedux(MyComponent);
// Используй Hook
function MyComponent() {
const data = useSelector(state => state.data);
const dispatch = useDispatch();
// ...
}
Однако HOC всё ещё полезны для:
- Обёртывания в провайдеры
- Защиты маршрутов
- Предоставления контекста
- Сложной трансформации пропсов
HOC — это мощный паттерн для переиспользования логики, но в современном React рекомендуется использовать Custom Hooks как первый вариант, а HOC как второй, для более сложных случаев.