form-patterns
Skill100 starsChanged 37 days agoApache-2.0View on GitHub
Patterns for forms with cascading/dependent selections in the gpustack-ui monorepo. Use when building a form where picking one field derives another (pick A → auto-pick B → write form), handling async option loading on modal open, or protecting against stale async results.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- anymasoft/1c-aitheir own version, first seen Jul 17, 2026
- Arman-Kudaibergenov/1c-ai-development-kittheir own version, first seen Mar 4, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- Arman-Kudaibergenov/1c-ai-development-kit★ 163same name
- anymasoft/1c-ai★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository4 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- create-crud-pageSkill
- design-reviewSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 21
When "pick A then auto-pick B", match by rule and return undefined if no match — let the form field stay empty.
- Distinctive: line 102
When a controlled field's value comes from both user input and a programmatic default (e.g.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Form Patterns
Theme: keep cascading selections (pick A → derive B → write form) on a single, predictable path.
Accessing the form: form vs form.current
This is not absolute — it depends on the call site:
- Inside the form component (
forms/index.tsx), or anywhere holding aForm.useForm()instance → call it directly:form.setFieldsValue(...). - In the outer Drawer/Modal wrapper that opens the form and holds it via
ref={form}(const form = useRef(null)), driven by anopenprop → go through the ref:form.current?.setFieldsValue(...).
The reference template below is written for the Drawer-wrapper scenario (it reacts to open and owns the shared selection state), so it uses form.current? throughout. If you lift this logic into the form body with a useForm() instance, drop the .current.
1. No fallback for derived selection
When "pick A then auto-pick B", match by rule and return undefined if no match — let the form field stay empty. Do not silently fall back to list[0]; a fallback hides data issues and fakes a valid selection.
const findB = (key, list) =>
key ? list.find((x) => x.key === key) : undefined;For form fields, clear with undefined, not ''. In Ant Design undefined restores the placeholder; '' is treated as a real value.
2. Async race protection
For fetches triggered by a lifecycle entry (e.g. modal open), tag each invocation with a session ref. Discard stale results if the session rotated (modal closed and re-opened) before the response arrives.
const sessionRef = useRef(0);
useEffect(() => {
if (!open) {
sessionRef.current += 1;
return;
}
const session = ++sessionRef.current;
Promise.all([fetchA(), fetchB()]).then(([as, bs]) => {
if (sessionRef.current !== session) return;
applySelection(as.items[0], findB(as.items[0].key, bs.items));
});
}, [open]);3. Reference template
Two cascading selectors backed by a single shared state, with a single atomic write (state + form together):
type Selection = { a?: string; b?: number };
const [selection, setSelection] = useState<Selection>({});
const sessionRef = useRef(0);
const form = useRef<any>(null); // wrapper holds the form via <Form ref={form} /> — see "Accessing the form" above
const findB = (key, list) =>
key ? list.find((x) => x.key === key) : undefined;
// Single atomic write: state + form together.
const applySelection = (a, b) => {
setSelection({ a: a.name, b: b?.id });
form.current?.setFieldsValue({
field: b?.field,
spec: { ...currentSpec, ...b?.spec }
});
};
// Trigger 1: modal opened
useEffect(() => {
if (!open) {
sessionRef.current++;
setSelection({});
return;
}
const session = ++sessionRef.current;
Promise.all([fetchA(), fetchB()]).then(([as, bs]) => {
if (sessionRef.current !== session) return;
const first = as.items[0];
applySelection(first, findB(first.key, bs.items));
});
}, [open]);
// Trigger 2: user picks A
const handleAChange = (a) => {
applySelection(a, findB(a.key, listB));
};
// Trigger 3: user picks B
const handleBChange = (b) => {
setSelection((prev) => ({ ...prev, b: b.id }));
form.current?.setFieldsValue({ ...b.fields });
};4. Controlled input with derived fields
When a controlled field's value comes from both user input and a programmatic default (e.g. a percentage picked on a slider, and a default seeded on select / mode-switch), funnel both through one commit function — don't duplicate "write field + recompute derived" per call site.
- The
Form.Item-bound input'sonChange(value)forwards the value to the commit fn (the field is antd-bound, but pass the value explicitly so the default path can reuse the same fn instead of reading the store). - Seed defaults by calling the same commit fn with the computed value.
- Separate the commit action (write field + recompute dependents) from the render-derive (read the field → recompute dependents). Keeping the derive standalone lets it re-run on reload/edit where there's no user event.
// commit action — slider onChange AND default both call this
const commitRatio = (value: number) => {
form.setFieldsValue({ spec: { resources: { ratio: value, cores: 100 } } });
rescaleDerived(); // reads ratio from the form, sets the disabled cpu/ram
};
// render-derive — also called from the edit/reload effect
const rescaleDerived = () => {
const ratio = form.getFieldValue(['spec', 'resources', 'ratio']);
form.setFieldsValue({
spec: {
resources: {
cpu: floorScale(unit.cpu, ratio),
ram: floorScale(unit.ram, ratio)
}
}
});
};
// default seeding reuses the commit fn — one path, not a second copy
const applyDefaults = (item) => commitRatio(Math.min(10, item.maxRatio) || 10);Related
- Module/file structure for forms lives in the create-crud-page skill (section 3).
- Required-field validation: use
getRuleMessage.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

