Compartment
Using compartments to create isolated plugin environments
Compartments are Neodrag v3’s solution for reactive plugin updates. They provide manual, predictable control over when your draggable behavior changes.
What Are Compartments?
Compartments are reactive containers that hold a single plugin (or null) and update when their dependencies change.
Key Rule: One plugin per compartment - they don’t hold arrays of plugins.
How They Work
Compartments wrap your framework’s reactive system:
Vanilla: Manual state management with explicit updates
Why Compartments?
V2 had automatic reactivity but caused problems:
- Performance Issues: Entire plugin arrays recreated on every change
- Memory Leaks: Old plugin instances weren’t properly cleaned up
- Unpredictable Updates: Hard to control when updates happened
- Over-Engineering: Simple static configs got reactive treatment they didn’t need
Compartments solve this with manual control, predictable updates, and better performance.
Basic Usage
import { Draggable, axis, Compartment } from '@neodrag/vanilla';
let currentAxis = 'x';
const axisComp = new Compartment(() => axis(currentAxis));
const instance = new Draggable(element, () => [axisComp]);
// Manual update required
function switchAxis() {
currentAxis = 'y';
axisComp.current = axis(currentAxis);
}
Multiple Compartments
Mix static plugins with reactive compartments:
let currentAxis = 'x';
let gridSize = 20;
let enableBounds = false;
// Reactive compartments
const axisComp = new Compartment(() => axis(currentAxis));
const gridComp = new Compartment(() => grid([gridSize, gridSize]));
const boundsComp = new Compartment(() =>
enableBounds ? bounds(BoundsFrom.parent()) : null,
);
// Static plugins (never change)
const staticPlugins = [events({ onDrag: console.log })];
const instance = new Draggable(element, () => [
...staticPlugins,
axisComp,
gridComp,
boundsComp,
]);
When to Use Compartments
Use Compartments For:
- Dynamic values that change from user interaction (sliders, dropdowns, toggles)
- Conditional plugin logic (enable/disable features based on state)
- Complex computed values with multiple dependencies
Don’t Use Compartments For:
- Static values that never change (just use direct plugin arrays)
- Simple boolean toggles (use direct conditionals instead)
Examples:
// ✅ Good - dynamic values
let userChoice = 'x';
let gridSize = 20;
const axisComp = new Compartment(() => axis(userChoice));
const gridComp = new Compartment(() => grid([gridSize, gridSize]));
// ❌ Bad - static values
const staticComp = new Compartment(() => bounds(BoundsFrom.parent()));
// ✅ Better - direct usage
const staticPlugins = [bounds(BoundsFrom.parent())];
// Manual updates required for vanilla
function updateUserChoice(newChoice) {
userChoice = newChoice;
axisComp.current = axis(userChoice);
}
Debugging Compartments
Common Issues
Compartment not updating:
// ❌ Wrong - forgot to update
function changeAxis(newAxis) {
currentAxis = newAxis;
// Missing: axisComp.current = axis(currentAxis);
}
// ✅ Correct - manual update
function changeAxis(newAxis) {
currentAxis = newAxis;
axisComp.current = axis(currentAxis);
}
Debug Logging
Add logging to see when compartments update:
const axisComp = new Compartment(() => {
console.log('Axis compartment updating:', currentAxis);
return axis(currentAxis);
});
// Don't forget manual updates
function changeAxis(newAxis) {
currentAxis = newAxis;
axisComp.current = axis(currentAxis);
}