@pmndrs/uikit
Version:
Build performant 3D user interfaces with Three.js and yoga.
60 lines (59 loc) • 2.4 kB
JavaScript
import { effect, untracked } from '@preact/signals-core';
import { abortableEffect } from '../utils.js';
export function setupImmediateProperties(propertiesSignal, activeSignal, hasProperty, setProperty, abortSignal) {
let active = false;
let currentProperties;
let propertySubscriptions = {};
//the following 2 effects are seperated so that the cleanup call only happens when active changes from true to false
//or everything is cleaned up because the component is destroyed
abortableEffect(() => {
const newProperties = propertiesSignal.value;
if (active) {
applyProperties(hasProperty, newProperties, currentProperties, propertySubscriptions, setProperty);
}
currentProperties = newProperties;
}, abortSignal);
abortableEffect(() => {
active = activeSignal.value;
if (!active) {
return;
}
if (currentProperties == null) {
return;
}
//(re-)write all current properties since the object is (re-)activiated it might not have its values set
applyProperties(hasProperty, currentProperties, undefined, propertySubscriptions, setProperty);
return () => {
unsubscribeProperties(propertySubscriptions);
propertySubscriptions = {};
};
}, abortSignal);
}
function applyProperties(hasProperty, currentProperties, oldProperties, subscriptions, setProperty) {
const onNew = (key) =>
//subscribe and write property
(subscriptions[key] = effect(() => setProperty(key, currentProperties.read(key, undefined))));
const onDelete = (key) => {
//remove subscription
subscriptions[key]?.();
delete subscriptions[key];
//read is fine since we execute the compare in "untracked"
if (oldProperties.read(key, undefined) === undefined) {
//no need to set to undefined if already was undefined
return;
}
//reset property
setProperty(key, undefined);
};
const onChange = (key) => {
//unsubscribe old property
subscriptions[key]?.();
onNew(key);
};
untracked(() => currentProperties.filterCompare(hasProperty, oldProperties, onNew, onChange, onDelete));
}
function unsubscribeProperties(subscriptions) {
for (const key in subscriptions) {
subscriptions[key]();
}
}