Современный React все больше соответствует идеалам функционального программирования.
Ежедневно мы пользуемся подходами из мира ФП, зачастую даже не подозревая об этом.
Эти паттерны плотно укоренились в сознании фронтенд-разработчиков, делая наш код значительно чище, читаемее и предсказуемее.
Вот лишь некоторые из них:
Декларативный код. Мы не говорим как рендерить, мы говорим что.
UI = f(state). Интерфейс – функция от состояния.
Композиция. Сложные компоненты собираются из простых независимых блоков.
Мемоизация. Функцию можно переиспользовать, пока данные не изменятся.
И так далее.

Программирование в функциональном стиле может быть не такой простой задачей, особенно для человека, не знакомого с этой парадигмой.
Обилие математических терминов, жесткие рамки и ограничения, непонятные формы записи и взрывающие голову рекурсии.
Но как только вы осознаете всю красоту и мощь функциональных паттернов – вы уже не сможете без них обойтись.
Сегодня мы углубимся в идеи функционального мира и рассмотрим один из классических паттернов – каррирование. Мы разберемся с тем, что это такое и как применить эти знания в React.
Каррирование
Каррирование – это процесс преобразования функции, которая принимает несколько аргументов, в набор функций, каждая из которых принимает один аргумент.
function sum(a: number, b: number) {
return a + b;
}
function curriedSum(a: number) {
return function(b: number) {
return a + b;
};
}
curriedSum(2)(3);
Часто вместе с каррированием обсуждается "частичное применение" – фиксирование части аргументов заранее.
Эти паттерны схожи, но не идентичны.
При каррировании каждый вызов может принимать строго один аргумент, а при частичном применении – несколько.
А что в React?
В современной React-разработке компонент в виде функции является стандартом. Он принимает пропсы на вход и возвращает JSX на выходе.
Можем ли мы применить принцип каррирования к props React-компонента? Зафиксировать часть значений, а остальные передать позднее.
В этой статье мы реализуем именно принцип частичного применения. Передавать один проп или несколько – решать вам.
Предлагаю отложить все объяснения на потом и сразу взглянуть пример:
import { FC } from 'react';
function partialProps<P extends Record<string, unknown>, K extends keyof P>(
Component: FC<P>,
partial: Pick<P, K>,
): FC<Omit<P, K>> {
return (props) => {
return <Component {...partial} {...(props as P)} />;
};
}
Теперь давайте подробнее рассмотрим из чего состоит данная функция:
P
– общий тип пропов исходного компонента.K extends keyof P
– множество ключей пропов, которые мы хотим зафиксировать.Pick<P, K>
берёт изP
только ключи изK
.Omit<P, K>
удаляет изP
все ключи изK
.Возвращаемым результатом этой функции будет новый компонент
FC<Omit<P, K>>
, который ожидает только оставшиеся поля.Во время рендера
partial
иprops
будут склеены в общий набор пропов, соответствующий типуP
.
Вот так просто у нас получилась функция частичного применения для пропов React-компонента.
Как по мне – выглядит очень лаконично, вполне в духе ФП!
Теперь пришло время рассмотреть несколько примеров того, как применить этот паттерн на практике.
Начнем с того, что перепишем изначальный пример с суммой двух чисел с использованием функции partialProps
:
type Props = {
a: number;
b: number;
};
const Sum = ({ a, b }: Props) => {
return <div>{a + b}</div>;
};
const PartialSum = partialProps(Component, { a: 1 });
export default function App() {
return (
<div>
<PartialSum b={3} />
</div>
);
}
Как мы видим, компонент Sum
больше не ждет, что мы передадим ему все необходимые значения сразу, а готов принимать их последовательно.
Далее рассмотрим достаточно часто случающийся случай. В вашей библиотеке компонентов наверняка есть варианты кнопок для разных ситуаций.
Цвет, размер, вариант – все это может быть сконфигурированно заранее с помощью функции partialProps
.
type ButtonProps = {
color: string;
size: "small" | "medium" | "large";
variant: "contained" | "outlined";
onClick?: () => void;
};
const Button: React.FC<ButtonProps> = ({
color,
size,
variant,
onClick,
children,
}) => {
const style = { backgroundColor: color };
const className = `${size} ${variant}`;
return (
<button
style={style}
onClick={onClick}
className={className}
>
{children}
</button>
);
};
const ImportantRedButton = partialProps(Button, {
color: "red",
size: "large",
variant: "contained",
});
export default function App() {
return (
<ImportantRedButton onClick={() => alert("Clicked!")}>
Delete
</ImportantRedButton>
);
};
Таких предсконфигурированных кнопок можно создать любое количество, задав им часть нужных параметров заранее.
Вот еще несколько примеров, где такая функция может быть полезна:
Предустановленный роутер / навигация.
Предварительно заполненные параметры API-запроса.
Предконфигурированные поля формы.
Преднастроенные визуальные темы.
Частичное применение параметров коллбэков.

Higher-Order Components
Если вы давно программируете на React, наверно, смогли заметить, что этот подход во многом совпадает по принципу с другим паттерном (кстати тоже из функционального программирования) — HOC (Higher‑Order Component).
Когда вы каррируете компонент — вы фактически создаете узкоспециализированный HOC, который подставляет пропсы.
С точки зрения реализации частичное применение пропсов — это частный случай HOC.
Я бы сказал, что тут нет конфликта, каждый решает свою специфическую задачу.
Однако у такого подхода есть и свои плюсы:
Простота и наглядность. Нет «магии» или сторонних эффектов.
Чистота. Нет необходимости явно указывать принимаемые параметры, как в HOC.
Строгая типизация. TypeScript сможет точно вывести какие поля остались свободными, а какие уже зафиксированы.
Заключение
Частичное применение и каррирование — это отличные паттерны, которые прекрасно ложатся на функциональную парадигму программирования в React!
Надеюсь, эта маленькая, но полезная, утилита поможет вам сделать код декларативнее и углубит ваши знания в функциональном программировании.
capfsb
Я думаю, что я бы все таки предпочел в проекте видеть простой специализированный компонент, это всего на пару строчек больше, зато не нужно в голове держать новую абстрацию partialProps и понятно даже новичку что происходит. Например:
AndreyVolfman Автор
Хорошая альтернатива, благодарю!