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

useSaga

Запускает сагу, привязанную к жизненному циклу компонента.

Сигнатура

function useSaga<TRes, TArgs extends any[]>(
saga: {
id: string;
onLoad?: (...args: TArgs) => Generator<any, TRes>;
onDispose?: (...args: TArgs) => Generator<any, void>;
},
args: TArgs,
options?: {
operationOptions?: {
updateStrategy: (operation: AsyncOperation) => AsyncOperation;
};
}
): {
operationId: OperationId<TRes, TArgs>;
reload: () => void;
};

Параметры

ПараметрТипОписание
saga.idstringОбязательный ID операции (должен быть уникальным для каждого экземпляра компонента)
saga.onLoadSaga?Сага, выполняемая на mount и при изменении args
saga.onDisposeSaga?Сага очистки, выполняемая перед повторным onLoad или unmount
argsTArgsМассив зависимостей (как в useEffect)
options.operationOptions.updateStrategyfunction?Функция для модификации операции перед сохранением

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

ПолеТипОписание
operationIdOperationIdID для подписки через useOperation или Operation
reload() => voidФункция для принудительного перезапуска саги

Описание

useSaga — это основной способ запуска асинхронной логики в компонентах:

  1. На mount — выполняется onLoad
  2. При изменении args — текущий onLoad отменяется, выполняется onDispose, затем новый onLoad
  3. На unmountonLoad отменяется, выполняется onDispose

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

import { useSaga, Operation } from '@iiiristram/sagun';
import { call } from 'typed-redux-saga';

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

return (
<Suspense fallback={<Spinner />}>
<Operation operationId={operationId}>
{(op) => <div>{op.result?.name}</div>}
</Operation>
</Suspense>
);
}

С очисткой

function LiveData({ streamId }) {
const { operationId } = useSaga({
id: `live-data-${streamId}`,
onLoad: function* () {
const subscription = yield* call(api.subscribe, streamId);
return subscription;
},
onDispose: function* () {
yield* call(api.unsubscribe, streamId);
}
}, [streamId]);

return (
<Operation operationId={operationId}>
{(op) => <DataView data={op.result} />}
</Operation>
);
}

С принудительной перезагрузкой

function DataWithRefresh({ id }) {
const { operationId, reload } = useSaga({
id: `data-${id}`,
onLoad: function* () {
return yield* call(api.getData, id);
}
}, [id]);

return (
<div>
<button onClick={reload}>Обновить</button>
<Operation operationId={operationId}>
{(op) => <div>{op.result}</div>}
</Operation>
</div>
);
}

С updateStrategy

Используйте updateStrategy для модификации операции перед сохранением (например, для пагинации):

import { select } from 'typed-redux-saga';

function PaginatedList({ page }) {
const { operationId } = useSaga({
id: 'paginated-list',
onLoad: function* (pageNum) {
return yield* call(api.getItems, pageNum);
}
}, [page], {
operationOptions: {
updateStrategy: function* (next) {
// Получить предыдущее состояние
const prev = yield* select(state =>
state.asyncOperations.get(next.id)
);

// Объединить результаты
return {
...next,
result: prev?.result && next.result
? [...prev.result, ...next.result]
: next.result || prev?.result,
};
}
}
});

return (
<Operation operationId={operationId}>
{(op) => <ItemList items={op.result} />}
</Operation>
);
}

Важно

  • id обязателен — должен быть уникальным для каждого экземпляра компонента (например, item-${id} для элементов списка)
  • onLoad отменяется при изменении args или unmount
  • onDispose выполняется до конца (не отменяется)
  • Используйте try/finally в onLoad для гарантированной очистки при отмене
Почему id обязателен?

Начиная с React v18, поведение Suspense изменилось — React может сбрасывать состояние компонента. Стабильный id необходим для корректной работы. См. React issue #24669.

useSagaUnsafe (deprecated)

Для обратной совместимости доступен useSagaUnsafe, где id опционален:

import { useSagaUnsafe } from '@iiiristram/sagun';

// id опционален, но не рекомендуется для новых компонентов
const { operationId } = useSagaUnsafe({
onLoad: function* () {
return yield* call(api.getData);
}
}, []);
warning

useSagaUnsafe помечен как deprecated. Используйте useSaga с обязательным id.

См. также

  • useOperation — подписка на операцию
  • Operation — компонент отображения
  • useService — для сложной логики используйте сервисы