Skip to content

Commit 0e08e58

Browse files
committed
OneOfObjectArrayForm example
1 parent d557210 commit 0e08e58

File tree

4 files changed

+168
-2
lines changed

4 files changed

+168
-2
lines changed

Todo.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
- Nested validators in child forms to improve validation performance
55
- Typed FormInput type prop
66
- Combine array helpers into one object? This is usefull to pass to other components
7+
- Require index for array fields

example/src/App.tsx

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import {
1212
Listener,
1313
FormTextArea,
1414
ChildForm,
15-
yupValidator
15+
yupValidator,
16+
useArrayForm,
17+
memberCopy
1618
} from "typed-react-form";
1719
import { VisualRender } from "./VisualRender";
1820
import * as yup from "yup";
@@ -90,6 +92,40 @@ const TodoListSchema = yup.object({
9092
)
9193
});
9294

95+
export function ArrayTest() {
96+
const [values, setValues] = useState({ name: "a list", items: ["asdf"] });
97+
const form = useForm(values);
98+
const arrayForm = useArrayForm(form, "items");
99+
100+
return (
101+
<form
102+
onSubmit={(ev) => {
103+
ev.preventDefault();
104+
console.log(form.values);
105+
setValues(memberCopy(form.values));
106+
}}
107+
>
108+
<p>Name</p>
109+
<FormInput form={form} name="name" />
110+
<p>Items</p>
111+
<ul>
112+
{arrayForm.values.map((_, i) => (
113+
<li>
114+
<FormInput key={i} form={arrayForm.form} name={i} />
115+
<button type="button" onClick={() => arrayForm.remove(i)}>
116+
Remove
117+
</button>
118+
</li>
119+
))}
120+
</ul>
121+
<button type="button" onClick={() => arrayForm.append("")}>
122+
Add item
123+
</button>
124+
<button>Submit</button>
125+
</form>
126+
);
127+
}
128+
93129
export function Form() {
94130
const form = useForm(
95131
initialValues, // <- Default values, may change

example/src/OneOfObjectArrayForm.tsx

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import React from "react";
2+
import { AnyListener, ArrayForm, FormInput, FormState, useChildForm, useForm, useListener } from "typed-react-form";
3+
4+
interface Apple {
5+
type: "apple";
6+
color: string;
7+
}
8+
9+
interface Bread {
10+
type: "bread";
11+
size: number;
12+
}
13+
14+
type FormDataObject = Apple | Bread;
15+
16+
interface FormData {
17+
objects: FormDataObject[];
18+
}
19+
20+
export default function OneOfObjectArrayForm() {
21+
const form = useForm<FormData>({
22+
objects: [
23+
{ type: "apple", color: "#ff0000" },
24+
{ type: "bread", size: 200 }
25+
]
26+
});
27+
return (
28+
<form
29+
style={{ margin: "0.5em" }}
30+
onReset={() => form.resetAll()}
31+
onSubmit={async (ev) => {
32+
ev.preventDefault();
33+
form.setState({ isSubmitting: true });
34+
await new Promise((res) => setTimeout(res, 500));
35+
form.setState({ isSubmitting: false });
36+
console.log(form.values);
37+
form.setDefaultValues(form.values);
38+
}}
39+
>
40+
<ArrayForm
41+
form={form}
42+
name="objects"
43+
render={({ form, values, append, remove }) => (
44+
<>
45+
<ul>
46+
{values.map((_, i) => (
47+
<li key={i}>
48+
{/* Make sure to use the form from ArrayForm! */}
49+
<BreadOrAppleForm parent={form} index={i} remove={() => remove(i)} />
50+
</li>
51+
))}
52+
</ul>
53+
<hr />
54+
<button type="button" onClick={() => append({ type: "apple", color: "#ff0000" })}>
55+
Add Apple
56+
</button>
57+
<button type="button" onClick={() => append({ type: "bread", size: 200 })}>
58+
Add Bread
59+
</button>
60+
</>
61+
)}
62+
/>
63+
<AnyListener
64+
form={form}
65+
render={({ values, dirty }) => (
66+
<pre>
67+
{dirty ? "MODIFIED" : "UNMODIFIED"}
68+
<br />
69+
{JSON.stringify(values, null, 2)}
70+
</pre>
71+
)}
72+
/>
73+
<button>Submit!</button>
74+
<button type="reset">Reset</button>
75+
</form>
76+
);
77+
}
78+
79+
function BreadOrAppleForm(props: { parent: FormState<FormDataObject[]>; index: number; remove: () => void }) {
80+
// Create a new child form with the array as the parent and index as the key
81+
const form = useChildForm(props.parent, props.index);
82+
// Listen for changes on the 'type' field, which contains 'apple' or 'bread'. This component will rerender when it changes
83+
const { value: type } = useListener(form, "type");
84+
return (
85+
<div style={{ background: "#0001", padding: "1em", margin: "1em" }}>
86+
{/* A select input that sets new form values when a new object type (apple or bread) was chosen */}
87+
<label>Object type: </label>
88+
<select
89+
value={type}
90+
onChange={(ev) => {
91+
if (ev.target.value === "apple") form.setValues({ type: "apple", color: "red" });
92+
else if (ev.target.value === "bread") form.setValues({ type: "bread", size: 200 });
93+
}}
94+
>
95+
<option value="apple">Apple</option>
96+
<option value="bread">Bread</option>
97+
</select>
98+
99+
{/* Render AppleForm when the type is 'apple'. When type is 'bread', render BreadForm */}
100+
{type === "apple" ? <AppleForm form={form as FormState<Apple>} /> : <BreadForm form={form as FormState<Bread>} />}
101+
102+
{/* Remove this item from the list */}
103+
<button type="button" onClick={() => props.remove()}>
104+
Remove
105+
</button>
106+
</div>
107+
);
108+
}
109+
110+
function AppleForm({ form }: { form: FormState<Apple> }) {
111+
return (
112+
<div>
113+
<h4>Apple editor</h4>
114+
<p>Select the color of your apple</p>
115+
<FormInput form={form} type="color" name="color" />
116+
</div>
117+
);
118+
}
119+
120+
function BreadForm({ form }: { form: FormState<Bread> }) {
121+
return (
122+
<div>
123+
<h4>Bread editor</h4>
124+
<p>Select the size of your bread</p>
125+
<FormInput form={form} type="number" name="size" />
126+
</div>
127+
);
128+
}

example/src/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import "./index.css";
22

33
import React from "react";
44
import ReactDOM from "react-dom";
5-
import App from "./App";
5+
// import App from "./App";
6+
import App from "./OneOfObjectArrayForm";
67

78
ReactDOM.render(<App />, document.getElementById("root"));

0 commit comments

Comments
 (0)