@@ -68,56 +68,113 @@ The `validate` function checks a value against a `Rule` supporting: `required`,
6868
6969``` mermaid
7070graph TD
71- subgraph Form["<Form>"]
71+ subgraph Form["<Form> Provider "]
7272 FI["FormInstance (useRef)"]
7373 FIC["FormInstanceContext.Provider"]
7474 FOC["FormOptionsContext.Provider"]
7575 end
7676
77- subgraph FormItem1["<Form.Item name='username'>"]
78- SUB1["subscribe(listener)"]
79- CE1["cloneElement(child, {value, onChange, onBlur})"]
80- ERR1["Error display with Transition"]
77+ subgraph FormItem1["Form.Item (name='username')"]
78+ CE1["cloneElement → {value, onChange, onBlur}"]
8179 end
8280
83- subgraph FormItem2["<Form.Item name='password'>"]
84- SUB2["subscribe(listener)"]
85- CE2["cloneElement(child, {value, onChange, onBlur})"]
86- ERR2["Error display with Transition"]
81+ subgraph FormItem2["Form.Item (name='password')"]
82+ CE2["cloneElement → {value, onChange, onBlur}"]
83+ end
84+
85+ subgraph Input1["Input (controlled by value prop)"]
86+ V1["value: from useState"]
87+ end
88+
89+ subgraph Input2["Input (controlled by value prop)"]
90+ V2["value: from useState"]
8791 end
8892
8993 FI --> FIC
94+ FI --> FOC
9095 FIC --> FormItem1
9196 FIC --> FormItem2
9297 FOC --> FormItem1
9398 FOC --> FormItem2
99+
100+ FormItem1 --> CE1 --> V1
101+ FormItem2 --> CE2 --> V2
102+ ```
103+
104+ ### Subscribe/Notify Pattern (Pub/Sub)
105+
106+ ``` mermaid
107+ graph LR
108+ subgraph FormInstance["FormInstance (Central Store)"]
109+ VALUES["values: { username: '', password: '' }"]
110+ ERRORS["errors: { email: ['Invalid email'] }"]
111+ RULES["rules: { username: [...], email: [...] }"]
112+ LISTENERS["listeners: [listener1, listener2, ...]"]
113+ NOTIFY["notify(name)"]
114+ end
115+
116+ subgraph FormItem_A["Form.Item(name='username')"]
117+ SUB_A["subscribe(callback)"]
118+ CALLBACK_A["callback(n) { if n==='username' setValue() }"]
119+ end
120+
121+ subgraph FormItem_B["Form.Item(name='email')"]
122+ SUB_B["subscribe(callback)"]
123+ CALLBACK_B["callback(n) { if n==='email' setValue() }"]
124+ end
125+
126+ subgraph FormItem_C["Form.Item(name='password')"]
127+ SUB_C["subscribe(callback)"]
128+ CALLBACK_C["callback(n) { if n==='password' setValue() }"]
129+ end
130+
131+ NOTIFY -->|"notify('email')"| LISTENERS
132+ LISTENERS -->|"→ listener_A"| CALLBACK_A
133+ LISTENERS -->|"→ listener_B"| CALLBACK_B
134+ LISTENERS -->|"→ listener_C"| CALLBACK_C
135+
136+ SUB_A -.->|"adds listener_A"| LISTENERS
137+ SUB_B -.->|"adds listener_B"| LISTENERS
138+ SUB_C -.->|"adds listener_C"| LISTENERS
139+
140+ style CALLBACK_A fill:#ffcccc
141+ style CALLBACK_B fill:#ccffcc
142+ style CALLBACK_C fill:#ffcccc
94143```
95144
96- ### Data Flow: User Input
145+ ### Data Flow: User Input → UI Update (Controlled)
97146
98147``` mermaid
99148sequenceDiagram
100149 participant User
101- participant Input as Child Input
150+ participant Input as < Input />
102151 participant Item as Form.Item
103152 participant Store as FormInstance
153+ participant Item2 as Form.Item (other)
104154
105- Note over Item: On mount : subscribe + setFieldRules
155+ Note over Item: Mount : subscribe(listener) + setFieldRules(name, rules)
106156
107- User->>Input: Types a character
108- Input->>Item: onChange fires
109- Item->>Store: setFieldValue(name, value)
110- Store->>Store: notify(name)
111- Store->>Item: Listener callback fires
112- Item->>Item: setValue → re-render
113- Item->>Input: cloneElement with new value
157+ User->>Input: Types "a"
158+ Input->>Item: onChange("a")
159+ Item->>Store: setFieldValue("field", "a")
160+ Store->>Store: values["field"] = "a"
161+ Store->>Store: notify("field")
162+ Store->>Item: listener("field") fires
163+ Store->>Item2: listener("field") fires
164+
165+ Note over Item2: n === "field" but name !== "field"<br/>→ ignore, no update
166+
167+ Item->>Item: setValue("a") → re-render
168+ Item->>Input: cloneElement injects new value="a"
169+
170+ Note over Input: Controlled: displays "a"
114171
115172 alt validateTrigger === "onChange"
116- Item->>Store: validateField(name )
117- Store->>Store: run rules via validate()
118- Store->>Store: setFieldError + notify
119- Store->>Item: Listener fires again
120- Item->>Item: setError → show/hide error
173+ Item->>Store: validateField("field" )
174+ Store->>Store: run validate() on rules
175+ Store->>Store: setFieldError + notify("field")
176+ Store->>Item: listener fires again
177+ Item->>Item: setError() → show/hide error
121178 end
122179```
123180
@@ -133,7 +190,7 @@ sequenceDiagram
133190 User->>Form: Submit
134191 Form->>Form: e.preventDefault()
135192 Form->>Store: validateFields()
136- Store->>Store: validateField() for each rule set
193+ Store->>Store: validateField() for each field with rules
137194 Store->>Items: notify each field name
138195 Items->>Items: Update error states
139196
@@ -161,7 +218,28 @@ sequenceDiagram
161218 Store->>Store: errors = {}
162219 Store->>Store: values = deepCopy(initValues)
163220 Store->>Items: notify("*")
164- Items->>Items: All items re-read value and error → re-render
221+ Items->>Items: All items: name === "*" or n === "*" → true<br/>→ setValue() + setError() → re-render
222+ ```
223+
224+ ### Controlled Component Pattern
225+
226+ ``` mermaid
227+ graph TD
228+ subgraph FormItem["Form.Item"]
229+ STATE["useState<br/>value, error"]
230+ ONCHANGE["onChange(...args)<br/>form.setFieldValue()<br/>form.validateField()"]
231+ CLONE["React.cloneElement<br/>{ value, onChange, onBlur }"]
232+ end
233+
234+ subgraph Child["Child Input"]
235+ PROP["value prop<br/>(controlled)"]
236+ INTERNAL["no internal state used"]
237+ end
238+
239+ STATE -->|"on mount"| CLONE
240+ ONCHANGE -->|"on input"| CLONE
241+ CLONE -->|"props"| PROP
242+ PROP -->|"user types"| ONCHANGE
165243```
166244
167245### Validation Rules
0 commit comments