SSR и тесты
Поскольку под капотом query — обычный effector, fork() + allSettled() работают как обычно — без специальных тестовых утилит.
Тестирование запроса
import { fork, allSettled } from 'effector';
const scope = fork();
await allSettled(query.start, { scope, params: 1 });
expect(scope.getState(query.$data)).toEqual(/* ... */);SSR
const scope = fork();
await allSettled(query.start, { scope, params: req.params });
const html = renderToString(/* app */, scope);
const serialized = serialize(scope); // effector serialize — $data / $status / …Биндинги учитывают scope: React через <Provider value={scope}>, Vue через EffectorScopePlugin({ scope }).
Перенос кэша (dehydrate / hydrate)
serialize(scope) сохраняет состояние сторов, но кэш запроса (dedupe / staleAfter) живёт вне scope и туда не попадает. dehydrate снимает его снапшот, hydrate восстанавливает на клиенте — и закэшированные параметры дают хит вместо перезапроса:
// сервер — рядом с serialize(scope)
const cache = inMemoryCache();
const todos = createQuery({ effect: fetchTodosFx, cache: { adapter: cache } });
// … прогоняем запросы под scope …
const payload = { values: serialize(scope), cache: dehydrate(cache) };
// клиент
hydrate(cache, payload.cache); // греем кэш (storedAt сохраняется → staleAfter стареет корректно)
const scope = fork({ values: payload.values }); // $data восстановлен — без мигания загрузкиДегидрируются только адаптеры, умеющие перечислять записи (например inMemoryCache); web-storage адаптеры персистят себя сами.
Персист на клиенте
Два взаимодополняющих способа пережить перезагрузку в браузере:
Слой кэша — используйте
localStorageCache/sessionStorageCacheкак адаптер; кэш запроса переживает перезагрузку (аversionинвалидирует старые данные).Слой стора — персистите
$dataнапрямую черезeffector-storage:tsimport { persist } from 'effector-storage/local'; persist({ store: todosQuery.$data as StoreWritable<Todo[] | null>, key: 'todos:data' });(
$dataв публичном типе read-only, но писабельный в рантайме — кастуйте дляpersist.)
Полный рабочий поток: examples/ssr.ts.
Заметки
- Sourced-конфиг (
Storeдляconcurrency/retry.times/cache.staleAfter/enabled) fork-корректен — каждый scope видит своё значение. - Адаптеры кэша держат состояние вне scope effector; для изолированного SSR создавайте запросы на каждый запрос (как обычно) или передавайте свежий адаптер.
- In-flight
AbortController-ы отслеживаются на инстанс query; не шарьте один инстанс между конкурентными SSR-запросами, если ещё и вызываетеcancel.