Skip to content

Commit c5c1de1

Browse files
committed
Add React form reducer validation playground
1 parent a79a51c commit c5c1de1

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

lib/components_guide_web/controllers/react_editor_controller.ex

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,80 @@ defmodule ComponentsGuideWeb.ReactEditorController do
265265
render_source(conn, source)
266266
end
267267

268+
def show(conn, %{"id" => "form-reducer-validation"}) do
269+
source = ~s"""
270+
function formDataFrom(element) {
271+
if (element instanceof HTMLFormElement) {
272+
return new FormData(element);
273+
}
274+
275+
const formData = new FormData();
276+
if (element instanceof HTMLInputElement) {
277+
formData.set(element.name, element.value);
278+
}
279+
return formData;
280+
}
281+
282+
function reducer(state, event) {
283+
if (event.type === "submit") {
284+
event.preventDefault();
285+
}
286+
287+
const errors = new Map(state.errors);
288+
for (const [name, value] of formDataFrom(event.target)) {
289+
// TODO: add more advanced validation here
290+
if (value.trim() === "") {
291+
errors.set(name, "Required");
292+
}
293+
}
294+
295+
return { ...state, errors };
296+
}
297+
298+
function Field({ name, label, error, type = "text" }) {
299+
const id = useId();
300+
return (
301+
<div class="flex items-center gap-2">
302+
<label for={id}>{label}</label>
303+
<input id={id} name={name} type={type} />
304+
<span class="italic">{error}</span>
305+
</div>
306+
);
307+
}
308+
309+
export default function App() {
310+
const [state, dispatch] = useReducer(reducer, { errors: new Map() });
311+
312+
return (
313+
<form onBlur={dispatch} onSubmit={dispatch} class="flex flex-col items-start gap-4">
314+
<p class="italic">Fields will individually validate on blur, or every field will validate on submit.</p>
315+
<fieldset class="flex flex-col gap-2">
316+
<Field
317+
name="firstName"
318+
label="First name"
319+
error={state.errors.get("firstName")}
320+
/>
321+
<Field
322+
name="lastName"
323+
label="Last name"
324+
error={state.errors.get("lastName")}
325+
/>
326+
<Field
327+
name="email"
328+
label="Email"
329+
type="email"
330+
error={state.errors.get("email")}
331+
/>
332+
</fieldset>
333+
<button class="px-3 py-1 bg-blue-300 rounded">Save</button>
334+
</form>
335+
);
336+
}
337+
"""
338+
339+
render_source(conn, source)
340+
end
341+
268342
def show(conn, %{"id" => "yieldmachine"}) do
269343
source = ~s"""
270344
import { start, on } from "https://unpkg.com/[email protected]/dist/yieldmachine.module.js"

0 commit comments

Comments
 (0)