Prefer Operators to Methods
In Effector, there are two ways to create a new unit from an existing one:
- Methods, e.g.
event.map(...),event.filter(...),store.map(...) - Operators, e.g.
combine(...)andsample(...)
In most cases, operators are more powerful and flexible than methods. You can add new features to operators without rewriting the code. Let us see how it works on a few examples.
combine
Let us say you have a derived Store to calculate a discount percentage for user:
const $discountPercentage = $user.map((user) => {
if (user.isPremium) return 20;
return 0;
});Some time later, you need to add a new feature: use current market conditions to calculate a discount percentage. In this case, you will need to completely rewrite the code:
const $discountPercentage = $user.map((user) => {
if (user.isPremium) return 20;
return 0;
});
const $discountPercentage = combine(
{ user: $user, market: $market },
({ user, market }) => {
if (user.isPremium) return 20;
if (market.isChristmas) return 10;
return 0;
}
);But if you use combine from the very beginning, you will be able to add a new feature without rewriting the code:
const $discountPercentage = combine(
{
user: $user,
market: $market,
},
({ user, market }) => {
if (user.isPremium) return 20;
if (market.isChristmas) return 10;
return 0;
}
);sample
It is even more noticeable when you need to filter an Event by a payload. Let us say you have an Event representing form submission and derived Event representing valid form submission:
const formSubmitted = createEvent();
const validFormSubmitted = formSubmitted.filter({
fn: (form) => {
return form.isValid();
},
});Some time later, you need to add a new feature: use external service to validate form instead of using isValid method. In this case, you will need to completely rewrite the code:
const validFormSubmitted = formSubmitted.filter({
fn: (form) => {
return form.isValid();
},
});
const validFormSubmitted = sample({
clock: formSubmitted,
source: $externalValidator,
filter: (validator, form) => validator(form),
});But if you use sample from the very beginning, you will be able to add a new feature without rewriting the code:
const validFormSubmitted = sample({
clock: formSubmitted,
filter: (form) => form.isValid(),
source: $externalValidator,
filter: (validator, form) => validator(form),
});With a sample we can go even further and add payload transformation just by adding a new argument:
const validFormSubmitted = sample({
clock: formSubmitted,
source: $externalValidator,
filter: (validator, form) => validator(form),
fn: (_, form) => form.toJson(),
});Cool, right? But it is not the end. We can add a new feature: use external Store to enrich the payload:
const validFormSubmitted = sample({
clock: formSubmitted,
source: {
validator: $externalValidator,
userName: $userName,
},
filter: ({ validator }, form) => validator(form),
fn: ({ userName }, form) => ({
...form.toJson(),
userName,
}),
});Summary
Prefer sample to event.filter/event.map and combine to store.map to make your code more extensible and transformable.
Exception
There is only one exception when you have to use method instead of operator: event.prepend(...) does not have an operator equivalent.