// Core effect - reactive side effects (internal)
// Cleanup tracking for core effects
///|
let core_current_cleanups : Ref[Array[() -> Unit]?] = Ref::new(None)
///|
/// Create a synchronous effect (runs immediately) - internal implementation
/// Returns a dispose function
fn core_render_effect(fn_ : () -> Unit) -> () -> Unit {
let cleanups : Array[() -> Unit] = []
let node : ReactiveNode = ReactiveNode::new(
ReactiveFlags::Watching.to_int() | ReactiveFlags::RecursedCheck.to_int(),
)
// Set up re-run callback for when dependencies change
node.effect_callback = Some(fn() {
let flags = node.flags.get_value()
let dirty_flag = ReactiveFlags::Dirty.to_int()
let pending_flag = ReactiveFlags::Pending.to_int()
// Check if we should run based on dirty/pending flags
let should_run = (flags & dirty_flag) != 0 || (flags & pending_flag) != 0
if should_run {
inc_cycle()
node.deps_tail = None
node.flags.set_value(
ReactiveFlags::Watching.to_int() | ReactiveFlags::RecursedCheck.to_int(),
)
// Run cleanups before re-running
core_run_cleanups(cleanups)
// Run effect with tracking
let prev_sub = set_active_sub(Some(node))
let prev_cleanups = core_current_cleanups.val
core_current_cleanups.val = Some(cleanups)
fn_()
core_current_cleanups.val = prev_cleanups
let _ = set_active_sub(prev_sub)
// Clear recursed check flag
let current_flags = node.flags.get_value()
node.flags.set_value(
current_flags & (ReactiveFlags::RecursedCheck.to_int() ^ -1),
)
purge_deps(node)
} else {
node.flags.set_value(ReactiveFlags::Watching.to_int())
}
})
let prev_sub = set_active_sub(Some(node))
// Link to parent effect if nested
if prev_sub is Some(ps) {
link(node, ps, 0)
}
// Track cleanups during initial run
let prev_cleanups = core_current_cleanups.val
core_current_cleanups.val = Some(cleanups)
fn_()
core_current_cleanups.val = prev_cleanups
// Restore previous subscriber
let _ = set_active_sub(prev_sub)
let flags = node.flags.get_value()
node.flags.set_value(flags & (ReactiveFlags::RecursedCheck.to_int() ^ -1))
// Return cleanup function
fn() {
core_run_cleanups(cleanups)
cleanup_effect_node(node)
}
}
///|
/// Run cleanup functions
fn core_run_cleanups(cleanups : Array[() -> Unit]) -> Unit {
// Run in reverse order (LIFO)
for i = cleanups.length() - 1; i >= 0; i = i - 1 {
cleanups[i]()
}
cleanups.clear()
}
///|
/// Clean up an effect node
fn cleanup_effect_node(node : ReactiveNode) -> Unit {
node.deps_tail = None
node.flags.set_value(ReactiveFlags::None.to_int())
node.effect_callback = None // Clear callback to prevent re-runs
purge_deps(node)
// Unlink from parent if any
if node.subs is Some(sub) {
let _ = unlink(sub, sub.sub)
}
}