Перейти к основному содержимому

useOperation

Подписывается на состояние операции.

Сигнатура

function useOperation<TRes, TArgs = any, TMeta = never, TErr = Error>(
options: {
operationId: OperationId<TRes, TArgs, TMeta, TErr>;
defaultState?: Partial<AsyncOperation<TRes, TArgs, TMeta, TErr>>;
suspense?: boolean;
}
): Partial<AsyncOperation<TRes, TArgs, TMeta, TErr>>;

// Статический метод — обязательно вызвать перед использованием
useOperation.setPath(path: (state: any) => State): void;

Параметры

ПараметрТипПо умолчаниюОписание
options.operationIdOperationIdID операции для подписки
options.defaultStatePartial<AsyncOperation>{ isLoading: true }Состояние по умолчанию, если операции нет в store
options.suspensebooleanfalseИнтеграция с React Suspense

Возвращаемое значение

Объект AsyncOperation:

ПолеТипОписание
idOperationIdID операции
isLoadingboolean?Операция выполняется
isErrorboolean?Операция завершилась с ошибкой
isBlockedboolean?Операция заблокирована
errorTErr?Ошибка, если произошла
resultTRes?Результат операции
argsTArgs?Аргументы, с которыми запущена операция
metaTMeta?Дополнительные метаданные

Описание

useOperation подписывается на операцию в Redux store и возвращает её текущее состояние. Компонент перерендерится при изменении состояния.

Настройка пути

Перед использованием необходимо указать путь к операциям в store:

// bootstrap.ts
useOperation.setPath(state => state.asyncOperations);

Базовый пример

import { useSaga, useOperation } from '@iiiristram/sagun';

function UserProfile({ userId }) {
const { operationId } = useSaga({
id: `user-${userId}`,
onLoad: function* () {
return yield* call(api.getUser, userId);
}
}, [userId]);

const operation = useOperation({ operationId });

if (operation.isLoading) {
return <Spinner />;
}

if (operation.isError) {
return <ErrorMessage error={operation.error} />;
}

return <div>Привет, {operation.result?.name}!</div>;
}

С операцией сервиса

import { useOperation, useServiceConsumer, getId } from '@iiiristram/sagun';

function UserActions() {
const { service, actions } = useServiceConsumer(UserService);

// Получить ID операции по методу
const operationId = getId(service.fetchUser);
const operation = useOperation({ operationId });

if (operation.isLoading) {
return <Button disabled>Загрузка...</Button>;
}

return (
<Button onClick={() => actions.fetchUser()}>
Обновить
</Button>
);
}

С Suspense

function UserData({ userId }) {
const { operationId } = useSaga({
id: `user-data-${userId}`,
onLoad: function* () {
return yield* call(api.getUser, userId);
}
}, [userId]);

// suspense: true — компонент "подвиснет" пока isLoading
const operation = useOperation({ operationId, suspense: true });

// Сюда попадаем только когда данные готовы
return <div>{operation.result?.name}</div>;
}

function Parent() {
return (
<Suspense fallback={<Spinner />}>
<UserData userId="123" />
</Suspense>
);
}

С defaultState

function OptionalData({ operationId }) {
const operation = useOperation({
operationId,
// Состояние по умолчанию, если операции нет в store
defaultState: {
isLoading: false,
result: []
}
});

return <List items={operation.result ?? []} />;
}

Vs Operation компонент

useOperation и Operation решают одну задачу разными способами:

// useOperation — ручное управление
function WithHook({ operationId }) {
const op = useOperation({ operationId });

if (op.isLoading) return <Spinner />;
if (op.isError) return <Error error={op.error} />;
return <Data result={op.result} />;
}

// useOperation с Suspense
function WithHookSuspense({ operationId }) {
const op = useOperation({ operationId, suspense: true });
return <Data result={op.result} />;
}

// Operation — декларативный подход с Suspense
function WithComponent({ operationId }) {
return (
<Suspense fallback={<Spinner />}>
<Operation operationId={operationId}>
{(op) => <Data result={op.result} />}
</Operation>
</Suspense>
);
}

Используйте useOperation когда:

  • Нужен доступ к isLoading/isError в логике компонента
  • Нужно кастомное defaultState
  • Нужно обрабатывать состояния особым образом

Используйте Operation когда:

  • Используете Suspense
  • Достаточно просто отобразить результат

См. также