Description
Background
There is an ancient feature in the effector
- when undefined
is returned from some reducer, it reads as skip update
command and store does not update its state.
The idea was to make every reducer a kind of filterMap
thing, which, in theory, can be handy sometimes, but in practice this feature is basically almost not used at all.
Its have few consequences:
undefined
basically cannot be used as a state of the store.createStore(undefined)
will throw.- Mapped stores are becoming "kind of stateful" - they do not have direct access to previous state, but still can do basically
return previousState
viaundefined
Basically, now it is a outdated and confusing design desicion, which is to be removed.
Explanation
As per effector's Releases Policy, every breaking change must be moved through deprecation period first.
But just deprecating it with a warning is not an option, as this way we basically will lose the possibility to use undefined
as a value in stores in any way - basically it will become sort of UB, which is weird.
This means, we need a graceful way to change the semantics of undefined
usage in reducers.
To do so, we need to introduce a separate configuration, which will set this behavior to specific option.
After that, in the effector 24, we will be able to change the default setting and deprecate skipVoid: true
.
And in the effector 25 finally get rid of this feature at all.
Examples
Derived stores
In effector 23 this code will show a warning, requiring the user to explicitly set skipVoid
setting in the configuration, while keeping the old filterMap
-like behavior.
No config
Should skip update and show a warning, requiring the user to explicitly provide the configuration
$store.map(s => s ? 42 : undefined)
Explicit skipVoid: false
This code will turn off filterMap
-like behavoir and will allow undefined
to pass further as a value
$store.map(s => s ? 42 : undefined, { skipVoid: false })
Explicit skipVoid: true
Will silenece the warning and keep current behaviour (skip of update)
$store.map(s => s ? 42 : undefined, { skipVoid: true })
Not affected case
The code, which does not use undefined
in any way, will not require any configuration
$store.map(s => s ? 42 : null)
The same applies to combine
Base stores
In effector 23 createStore
declaration with undefined as a base value should still throw, just like all versions before, but this time undefined
as a store value can be allowed as manual { skipVoid: false }
configuration.
Also, in reducers and samples, that are going to write undefined into store, warning, similiar to the one of the combine and map, should be shown
Not affected case
Those stores, which are not using undefined in any way, are not affected by this change
const $store = createStore<SomeObject | null>(null)
Store declaration with void or undefined
Throws immediatly with message like "To allow undefined value in store add { skipVoid: false } into the config
const $storeA = createStore()
const $storeB = createStore(undefined)
Writing undefined into store
Skips update and shows warning, requiring the user to explicitly set skipVoid
setting in the store configuration OR return previous state to explicitly skip update
$store.on(event, () => undefined)
$store.on(event, () => {
// nothing
})
sample({
clock: event,
fn: () => {
// nothing
},
target: $store,
})
Explicit skipVoid: false
Explicit skipVoid: false
allows to use undefined
as a value in this store in all cases
const $store = createStore(undefined, {skipVoid: false})
$store.on(event, () => undefined)
$store.on(event, () => {
// nothing
})
sample({
clock: event,
fn: () => {
// nothing
},
target: $store,
})
Explicit skipVoid: true
Keeps current behaviour AND mutes all the warnings