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:
makeModelsReviver
must receive an array of revivable models.- Reducing models will not "serialize" object values, such as Date.
You can use a tool like
devalue
to 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;
},
});