Reducing and reviving
- Reducing a model's instance
- Reviving a model's instance
- Configuring custom behaviors
Purpose
Reducing and reviving instances allow you to convert models' instances to a serializable object. This is useful when you cannot pass a complex object between two JavaScript programs, such as in Web Workers or when using Nuxt SSR features.
Using CLI
You can generate reducer and reviver using @foscia/cli.
- NPM
- YARN
- PNPM
- Bun
npx foscia make reducer
npx foscia make reviver
yarn foscia make reducer
yarn foscia make reviver
pnpm foscia make reducer
pnpm foscia make reviver
bun foscia make reducer
bun foscia make reviver
Reducing and reviving
Foscia provides built-in tools to reduce and revive models' instances with two
functions: makeModelsReducer and makeModelsReviver.
Those tools support instance's state, values and relations (circular or not) reducing and reviving out of the box.
import { makeModelsReducer, makeModelsReviver } from '@foscia/core';
import Post from './models/post';
import Comment from './models/comment';
const { reduce } = makeModelsReducer();
const { revive } = makeModelsReviver([Post, Comment]);
const myPost = new Post();
const json = JSON.stringify(reduce(myPost));
const myRevivedPost = revive(JSON.parse(json));
Notice that:
makeModelsRevivermust receive an array of revivable models.- Reducing models will not "serialize" object values, such as Date.
You can use a tool like
devalueto leverage this.
Custom behaviors
You can easily define custom reducing and reviving behaviors by defining
$reduce and $revive instance methods. Those custom behaviors can
support default state reducing/reviving or not, and can be strongly typed.
Keeping instance state
To provide a custom reducing/reviving behavior while keeping instance state
automatic reducing/reviving, you can use the provided data factory function
passed to $reduce. It will reduce internal data used by Foscia to correctly
revive instance state.
During $revive, data will contain your reduced data provided by $reduce.
import { attr, makeModel, ModelReduceTools } from '@foscia/core';
export default class Post extends makeModel('posts', {
title: attr(),
}) {
public foo = 'bar';
public $reduce({ data }: ModelReduceTools) {
return {
foo: this.foo,
// Foscia reducing should have priority over your custom data.
// It will reduce data with `$` prefixed keys, so avoid using those.
...data(this),
};
}
public $revive(data: any) {
this.foo = data.foo;
// Thanks to `data` use in `$reduce`, the post state is already revived
// and available inside `$revive`.
console.log(`Revived: ${this.title}`);
}
}
Destroying instance state
You can omit instance state reducing by not calling data function
inside your $reduce method. This can be useful when you need something
very specific, but be aware that no instance state will be restored
automatically (values, relations, $exists state, $original snapshot
or other instance properties).
import { attr, makeModel } from '@foscia/core';
export default class Post extends makeModel('posts', {
title: attr(),
}) {
public foo = 'bar';
public $reduce() {
return {
title: this.title,
foo: this.foo,
};
}
public $revive(data: any) {
this.title = data.title;
this.foo = data.foo;
}
}
Typechecking custom behaviors
If you want to ensure you do not miss any implemented methods or typing, you can
use Foscia provided interface ModelCanReduceRevive and types to strict
type your implementations of custom $reduce and $revive methods.
import { makeModel, ModelCanReduceRevive, ModelReduceTools, ModelReviveTools, ReducedModelInstanceCustomData } from '@foscia/core';
// You must declare a custom type which represent custom data format.
type PostReducedData = ReducedModelInstanceCustomData & {
// Your data typings...
foo: string;
};
export default class Post
extends makeModel('posts')
implements ModelCanReduceRevive<PostReducedData> {
public foo = 'bar';
public $reduce(tools: ModelReduceTools) {
return {
foo: this.foo,
...tools.data(this),
};
}
public $revive(data: PostReducedData, tools: ModelReviveTools) {
this.foo = data.foo;
}
}
Defining common custom behaviors
If you want to share a common behavior between some or all of your models,
you can use model composition to define common $reduce and $revive
methods.
With a composable
import { makeComposable, ModelReduceTools } from '@foscia/core';
export default makeComposable({
foo: 'bar',
$reduce({ data }: ModelReduceTools) {
return {
foo: this.foo,
...data.data(this),
};
},
$revive(data: any) {
this.foo = data.foo;
},
});
With a model factory
import { makeModelFactory, ModelReduceTools } from '@foscia/core';
export default makeModelFactory({}, {
foo: 'bar',
$reduce({ data }: ModelReduceTools) {
return {
foo: this.foo,
...data.data(this),
};
},
$revive(data: any) {
this.foo = data.foo;
},
});