Install
$ agentstack add skill-foxhound87-skills-mobx-react-form-observers-interceptors ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Skill: mobx-react-form-observers-interceptors
Mission
Guide the user through using MobX observers and interceptors in mobx-react-form to react to or intercept field value changes in real time.
Use this skill when the user needs to:
- Observe field value changes (post-commit)
- Intercept and potentially reject value changes (pre-commit)
- Transform values before they are committed
- Log all field changes in a debug panel
- Clean up observers/interceptors on unmount
Key Distinction
| Feature | observe() | intercept() | |---------|-----------|-------------| | When | After value change (post-commit) | Before value change (pre-commit) | | Can reject | No (read-only) | Yes (return null) | | Can modify | No | Yes (modify change object) | | Use case | Logging, side effects, reactions | Validation, transformation, rejection |
observe()
Fires after a value has been committed. Read-only — cannot modify or reject.
Field-level observe
form.$('firstName').observe(({ form, field, change }) => {
console.log(`${field.path} changed: "${change.oldValue}" → "${change.newValue}"`);
});
// Or using shorthand (default key = 'value')
form.$('email').observe(({ change }) => {
console.log('Email changed to:', change.newValue);
});
Form-level observe (by path)
form.observe({
path: 'password',
key: 'value',
call: ({ form, field, change }) => {
if (change.newValue.length > 0) {
form.$('passwordStrength').set(
change.newValue.length > 8 ? 'strong' : 'weak'
);
}
},
});
Observe other field properties
form.observe({
path: 'email',
key: 'focused', // observe focus changes
call: ({ change }) => {
console.log('Email focus:', change.newValue);
},
});
Observe fields map (additions/removals)
form.observe({
path: 'members',
key: 'fields', // observe the fields map
call: ({ change }) => {
console.log('Members changed:', change.type); // "add", "delete", "update"
},
});
intercept()
Fires before a value change is committed. Can modify or reject.
Field-level intercept
form.$('email').intercept(({ form, field, change }) => {
// Reject if no @
if (typeof change.newValue === 'string' && !change.newValue.includes('@')) {
return null; // reject the change
}
return change; // allow the change
});
Form-level intercept (by path)
form.intercept({
path: 'zipCode',
key: 'value',
call: ({ change }) => {
// Strip non-digits
change.newValue = String(change.newValue).replace(/\D/g, '');
return change; // must return the change object
},
});
Intercept return values
| Return | Behavior | |--------|----------| | change (modified or not) | Allow the mutation | | null | Reject the change entirely |
Using observers / interceptors Props
Define observers/interceptors in the field definition for automatic handling of dynamically added fields:
const observers = {
'club': [{
key: 'focused',
call: ({ form, field, change }) => {
console.log(`Club focus: ${change.newValue}`);
},
}],
'members[].hobbies[]': [{
key: 'touched',
call: ({ form, field, change }) => {
console.log(`Hobby ${field.path} touched: ${change.newValue}`);
},
}],
};
new Form({ fields, observers, interceptors, ... });
Or in unified mode:
const fields = {
email: {
observers: [{
key: 'value',
call: ({ change }) => console.log('Email:', change.newValue),
}],
interceptors: [{
key: 'value',
call: ({ change }) => {
if (typeof change.newValue === 'string' && !change.newValue.includes('@')) {
return null;
}
return change;
},
}],
},
};
Real-World: Change Log Panel
const [logs, setLogs] = useState([]);
useEffect(() => {
form.each((field) => {
field.observe(({ change, field }) => {
const entry = {
field: field.path,
from: change.oldValue,
to: change.newValue,
time: new Date().toLocaleTimeString(),
};
setLogs((prev) => [entry, ...prev].slice(0, 20));
});
});
return () => form.dispose();
}, [form]);
// Render:
{logs.map((log, i) => (
{log.time}
{log.field}
: {String(log.from)}
→ {String(log.to)}
))}
Real-World: Pre-commit Validation
useEffect(() => {
form.$('age').intercept(({ change }) => {
const num = Number(change.newValue);
if (isNaN(num) || num 150) {
return null; // reject invalid ages
}
return change;
});
return () => form.dispose();
}, [form]);
Cleanup with dispose()
Always clean up on unmount to prevent memory leaks:
useEffect(() => {
// Register observers/interceptors...
form.$('email').observe(/* ... */);
form.$('password').intercept(/* ... */);
return () => form.dispose(); // cleanup all
}, [form]);
Dispose all
form.dispose(); // removes all observers and interceptors recursively
Dispose single event
form.dispose({
type: 'observer',
path: 'password',
key: 'value',
});
// Or on selected field
form.$('password').dispose({
type: 'interceptor',
key: 'value',
});
Combining observe() with autorun()
For computed/derived values, combine with MobX autorun():
import { autorun } from 'mobx';
useEffect(() => {
const disposer = autorun(() => {
const firstName = form.$('firstName').value;
const lastName = form.$('lastName').value;
form.$('fullDisplay').set(`${firstName} ${lastName}`.trim() || '(empty)');
});
return () => {
form.dispose(); // removes observers
disposer(); // removes autorun
};
}, [form]);
Key Takeaways
- Post-commit (observe): Read-only listeners after value is written — logging, side effects.
- Pre-commit (intercept): Can modify or reject values before they're stored.
- Always return change: Interceptors must return the change object (possibly modified) or
null. - Props-based: Define
observers/interceptorsin field definitions for dynamic fields. - Dispose on unmount: Always call
form.dispose()to clean up all MobX events. - Dual cleanup:
form.dispose()for form events,disposer()for raw MobX reactions.
Related Skills
- [mobx-react-form-api](../mobx-react-form-api/SKILL.md) — Core API prerequisite
- [mobx-react-form-events](../mobx-react-form-events/SKILL.md) — Event hooks
- [mobx-react-form-computed](../mobx-react-form-computed/SKILL.md) — autorun and computed props
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: foxhound87
- Source: foxhound87/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.