@ -16,7 +16,8 @@ import {
STATE _SYMBOL ,
BLOCK _EFFECT ,
ROOT _EFFECT ,
LEGACY _DERIVED _PROP
LEGACY _DERIVED _PROP ,
DISCONNECTED
} from './constants.js' ;
import { flush _tasks } from './dom/task.js' ;
import { add _owner } from './dev/ownership.js' ;
@ -198,12 +199,15 @@ export function check_dirtiness(reaction) {
return true ;
}
var is _disconnected = ( flags & DISCONNECTED ) !== 0 ;
if ( ( flags & MAYBE _DIRTY ) !== 0 || ( is _dirty && is _unowned ) ) {
var dependencies = reaction . deps ;
if ( dependencies !== null ) {
var length = dependencies . length ;
var is _equal ;
var reactions ;
for ( var i = 0 ; i < length ; i ++ ) {
var dependency = dependencies [ i ] ;
@ -211,13 +215,13 @@ export function check_dirtiness(reaction) {
if ( ! is _dirty && check _dirtiness ( /** @type {import('#client').Derived} */ ( dependency ) ) ) {
is _equal = update _derived ( /** @type {import('#client').Derived} **/ ( dependency ) , true ) ;
}
var version = dependency . version ;
if ( is _unowned ) {
// If we're working with an unowned derived signal, then we need to check
// if our dependency write version is higher. If it is then we can assume
// that state has changed to a newer version and thus this unowned signal
// is also dirty.
var version = dependency . version ;
if ( version > /** @type {import('#client').Derived} */ ( reaction ) . version ) {
/** @type {import('#client').Derived} */ ( reaction ) . version = version ;
@ -228,7 +232,7 @@ export function check_dirtiness(reaction) {
// If we are working with an unowned signal as part of an effect (due to !current_skip_reaction)
// and the version hasn't changed, we still need to check that this reaction
// if linked to the dependency source – otherwise future updates will not be caught.
var reactions = dependency . reactions ;
reactions = dependency . reactions ;
if ( reactions === null ) {
dependency . reactions = [ reaction ] ;
} else {
@ -238,6 +242,20 @@ export function check_dirtiness(reaction) {
} else if ( ( reaction . f & DIRTY ) !== 0 ) {
// `signal` might now be dirty, as a result of calling `check_dirtiness` and/or `update_derived`
return true ;
} else if ( is _disconnected ) {
// It might be that the derived was was dereferenced from its dependencies but has now come alive again.
// In thise case, we need to re-attach it to the graph and mark it dirty if any of its dependencies have
// changed since.
if ( version > /** @type {import('#client').Derived} */ ( reaction ) . version ) {
/** @type {import('#client').Derived} */ ( reaction ) . version = version ;
is _dirty = true ;
}
reactions = dependency . reactions ;
if ( reactions === null ) {
dependency . reactions = [ reaction ] ;
} else if ( ! reactions . includes ( reaction ) ) {
reactions . push ( reaction ) ;
}
}
}
}
@ -246,6 +264,9 @@ export function check_dirtiness(reaction) {
if ( ! is _unowned ) {
set _signal _status ( reaction , CLEAN ) ;
}
if ( is _disconnected ) {
reaction . f ^= DISCONNECTED ;
}
}
return is _dirty ;
@ -422,9 +443,15 @@ function remove_reaction(signal, dependency) {
}
}
}
if ( reactions _length === 0 && ( dependency . f & UNOWNED ) !== 0 ) {
// If the signal is unowned then we need to make sure to change it to maybe dirty.
// If the derived has no reactions, then we can disconnect it from the graph,
// allowing it to either reconnect in the future, or be GC'd by the VM.
if ( reactions _length === 0 && ( dependency . f & DERIVED ) !== 0 ) {
set _signal _status ( dependency , MAYBE _DIRTY ) ;
// If we are working with a derived that is owned by an effect, then mark it as being
// disconnected.
if ( ( dependency . f & ( UNOWNED | DISCONNECTED ) ) === 0 ) {
dependency . f ^= DISCONNECTED ;
}
remove _reactions ( /** @type {import('#client').Derived} **/ ( dependency ) , 0 ) ;
}
}
@ -815,7 +842,7 @@ export function get(signal) {
) {
if ( current _dependencies === null ) {
current _dependencies = [ signal ] ;
} else {
} else if ( current _dependencies [ current _dependencies . length - 1 ] !== signal ) {
current _dependencies . push ( signal ) ;
}
}