Skip to content

Commit 4ff6854

Browse files
committed
Merge remote-tracking branch 'docs/main'
2 parents 4f3801a + ff40629 commit 4ff6854

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+4941
-0
lines changed

docs/.babelrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"presets": ["next/babel"],
3+
"plugins": [["styled-components", { "ssr": true }]]
4+
}

docs/.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
27+
# local env files
28+
.env.local
29+
.env.development.local
30+
.env.test.local
31+
.env.production.local
32+
33+
# vercel
34+
.vercel

docs/.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"tabWidth": 4,
3+
"useTabs": false,
4+
"printWidth": 120
5+
}

docs/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
yarn dev
10+
```
11+
12+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
13+
14+
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
15+
16+
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
17+
18+
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
19+
20+
## Learn More
21+
22+
To learn more about Next.js, take a look at the following resources:
23+
24+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
25+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
26+
27+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
28+
29+
## Deploy on Vercel
30+
31+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
32+
33+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

docs/articles/Array-fields.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# Array fields
2+
3+
To create dynamic array fields, you should use the [`ArrayForm`](/docs/ArrayForm) component or [`useArrayForm`](/docs/useArrayForm) hook. These are wrappers around [`useChildForm`](/docs/useChildForm) which provide useful functions and optimizations for arrays.
4+
5+
- [ArrayForm](/docs/ArrayForm)
6+
- [useArrayForm](/docs/useArrayForm)
7+
8+
If you have an array field with a constant size, you should probably just use [`ChildForm`](/docs/ChildForm). (See bottom for examples)
9+
10+
**Note on keys**: you **should** use the index as key, this seems against nature at first, but remember that this library does not rerender each time something in the array changes. When 2 array items get swapped, it does not rerender either, only when the array size changes, it rerenders. For this reason, it is not a problem (and it's recommended) to use index as the key. (This can change in the future)
11+
12+
## Dynamic array examples
13+
14+
✔️ **Dynamic string array field using `ArrayForm`**
15+
16+
```tsx
17+
function NotesList() {
18+
const form = useForm({
19+
notes: ["Do the dishes", "Go outside", "Drink some water"],
20+
});
21+
return (
22+
<form
23+
onSubmit={(ev) => {
24+
ev.preventDefault();
25+
console.log("submit", form.values);
26+
}}
27+
>
28+
<ArrayForm
29+
form={form}
30+
name="notes"
31+
render={({ form, values, append, remove }) => (
32+
<>
33+
{/* You SHOULD use index as key. See top for info. */}
34+
{values.map((_, i) => (
35+
<div key={i}>
36+
<FormInput form={form} name={i} />
37+
<button type="button" onClick={() => remove(i)}>
38+
Remove
39+
</button>
40+
</div>
41+
))}
42+
<button type="button" onClick={() => append("New note")}>
43+
Add note
44+
</button>
45+
</>
46+
)}
47+
/>
48+
<button>Submit</button>
49+
</form>
50+
);
51+
}
52+
```
53+
54+
✔️ **Dynamic object array field using `ArrayForm`**
55+
56+
Remember: this is all type checked!
57+
58+
```tsx
59+
function ShoppingListForm() {
60+
const form = useForm({
61+
title: "My shopping list",
62+
items: [{ name: "Coffee", amount: 1 }],
63+
});
64+
65+
return (
66+
<form
67+
onSubmit={(ev) => {
68+
ev.preventDefault();
69+
console.log("submit", form.values);
70+
}}
71+
>
72+
<h2>Title</h2>
73+
<FormInput form={form} type="text" name="title" />
74+
75+
{/* Create an array child form for the 'items' field */}
76+
<h2>Items</h2>
77+
<ArrayForm
78+
form={form}
79+
name="items"
80+
render={({ form, values, append, remove }) => (
81+
<>
82+
{/* This only rerenders when the whole array changes. */}
83+
84+
{values.map((_, i) => (
85+
// Create a child form for each item in the array, because each array item is an object.
86+
<ChildForm
87+
key={i} // You should index as key
88+
form={form} // Pass the parent form
89+
name={i} // Pass the current index as the name
90+
render={(form) => (
91+
<div>
92+
{/* Everything here is type-checked! */}
93+
<FormInput form={form} type="text" name="name" />
94+
<FormInput form={form} type="number" name="amount" />
95+
<button type="button" onClick={() => remove(i)}>
96+
Remove
97+
</button>
98+
</div>
99+
)}
100+
/>
101+
))}
102+
103+
{/* Use the append helper function to add an item to the array */}
104+
<button type="button" onClick={() => append({ name: "", amount: 1 })}>
105+
Add item
106+
</button>
107+
</>
108+
)}
109+
/>
110+
<button>Submit</button>
111+
</form>
112+
);
113+
}
114+
```
115+
116+
✔️ **Dynamic object array field with seperate component for each child form and using `useArrayForm`**
117+
118+
```tsx
119+
interface ShoppingListItem {
120+
name: string;
121+
amount: number;
122+
}
123+
interface ShoppingList {
124+
title: string;
125+
items: ShoppingListItem[];
126+
}
127+
128+
function ShoppingListForm() {
129+
const form =
130+
useForm <
131+
ShoppingList >
132+
{
133+
title: "My shopping list",
134+
items: [{ name: "Coffee", amount: 1 }],
135+
};
136+
return (
137+
<form
138+
onSubmit={(ev) => {
139+
ev.preventDefault();
140+
console.log("submit", form.values);
141+
}}
142+
>
143+
<h2>Title</h2>
144+
<FormInput form={form} type="text" name="title" />
145+
<h2>Items</h2>
146+
<ShoppingListItemsForm parent={form} />
147+
<button>Submit</button>
148+
</form>
149+
);
150+
}
151+
152+
function ShoppingListItemsForm(props: { parent: FormState<ShoppingList> }) {
153+
const { form, values, append, remove } = useArrayForm(props.parent, "items");
154+
// This component only rerenders when the whole array changes.
155+
return (
156+
<>
157+
{values.map((_, i) => (
158+
<ShoppingListItemForm parent={form} index={i} key={i} remove={remove} />
159+
))}
160+
<button type="button" onClick={() => append({ name: "", amount: 1 })}>
161+
Add item
162+
</button>
163+
</>
164+
);
165+
}
166+
167+
function ShoppingListItemForm(props: {
168+
parent: FormState<ShoppingListItem[]>;
169+
index: number;
170+
remove: (i: number) => void;
171+
}) {
172+
const form = useChildForm(props.parent, props.index);
173+
return (
174+
<div>
175+
<FormInput form={form} type="text" name="name" />
176+
<FormInput form={form} type="number" name="amount" />
177+
<button type="button" onClick={() => props.remove(props.index)}>
178+
Remove
179+
</button>
180+
</div>
181+
);
182+
}
183+
```
184+
185+
## Fixed array example
186+
187+
A fixed array always has the same size, [`ChildForm`](/docs/ChildForm) is used, and the index into the array is given using the name prop.
188+
189+
✔️ **Fixed array field containing strings**
190+
191+
```tsx
192+
function AnswerForm() {
193+
const form = useForm({
194+
// Always 3 items in array
195+
answers: ["", "", ""],
196+
});
197+
return (
198+
<form
199+
onSubmit={(ev) => {
200+
ev.preventDefault();
201+
console.log("submit", form.values);
202+
}}
203+
>
204+
<ChildForm
205+
parent={form}
206+
name="answers"
207+
render={(form) => (
208+
<div>
209+
{/* Use array index as field name */}
210+
<p>Answer 1</p>
211+
<FormInput form={form} name={0} type="text" />
212+
<p>Answer 2</p>
213+
<FormInput form={form} name={1} type="text" />
214+
<p>Answer 3</p>
215+
<FormInput form={form} name={2} type="text" />
216+
</div>
217+
)}
218+
/>
219+
<button>Submit</button>
220+
</form>
221+
);
222+
}
223+
```

docs/articles/ArrayForm.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# ArrayForm
2+
3+
A component that provides array manipulation functions and optimizations. This is a wrapper around [`useArrayForm`](/docs/useArrayForm). The only difference is that this component does not render its content when the array is null/undefined ([is togglable](/docs/Toggling-a-field)).
4+
5+
## Props
6+
7+
#### `form` **(required)**
8+
9+
The parent form which contains the array field to create a array child form for.
10+
11+
#### `name` **(required)**
12+
13+
The name of the array field in the parent form.
14+
15+
#### `render` **(required)**
16+
17+
A function that renders the array.
18+
19+
The render function provides an object parameter, containing the following fields:
20+
21+
- `form`: The child form associated with this array. Pass this to this child forms and input elements.
22+
- `values`: The array, you should `{map((e) => ...)}` this.
23+
- `setValues(values)`: A function to update all the array values at once.
24+
25+
**The object also contains helper functions to quickly manipulate the array field:**
26+
27+
- `remove(index)`: Function that removes a specific item at index in the array.
28+
- `clear()`: Function that clears the array.
29+
- `move(from, to)`: Function that moves an item in the array
30+
- `swap(index, newIndex)`: Function that swaps 2 items in the array.
31+
- `append(value)`: Function that appends an item to the end of the array.
32+
33+
## Usage
34+
35+
See [Array fields](/docs/Array-fields).
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
## Example: Submit button that disables when unmodified/error
2+
3+
![live updating form values](https://raw.githubusercontent.com/wiki/CodeStix/typed-react-form/images/submitbutton.gif)
4+
5+
```tsx
6+
function FormExample() {
7+
const form = useForm(
8+
{
9+
name: "John",
10+
},
11+
(values) => ({
12+
// Example validator
13+
name: values.name.length < 3 ? "Name must be longer" : undefined,
14+
})
15+
);
16+
return (
17+
<form
18+
onSubmit={(ev) => {
19+
ev.preventDefault();
20+
console.log("save", form.values);
21+
form.setDefaultValues(form.values);
22+
}}
23+
>
24+
<FormInput form={form} name="name" />
25+
{/* Listen for any change on the form */}
26+
<AnyListener form={form} render={(form) => <button disabled={!form.dirty || form.error}>Save</button>} />
27+
</form>
28+
);
29+
}
30+
```

docs/articles/ChildForm.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# ChildForm
2+
3+
Use the `ChildForm` component if you want to make fields inside an object field available to inputs. This is a wrapper around [`useChildForm`](/docs/useChildForm).
4+
5+
## Props
6+
7+
#### `form` (required)
8+
9+
The parent form which contains the object field to create a child form for.
10+
11+
#### `name` (required)
12+
13+
The name of the field in the parent form to create
14+
15+
#### `render` (required)
16+
17+
A function that renders, and creates inputs for the child form, which is passed as a parameter.
18+
19+
## Advanced
20+
21+
You can also use this component on array fields (index as name), but [`ArrayForm`](/docs/ArrayForm) or [`useArrayForm`](/docs/useArrayForm) is preferred when you want to create [dynamic array fields](/docs/Array-fields). It provides useful array manipulation functions and optimizations.
22+
23+
This component does not render its content when the form is empty (happens when its parent field has been assigned `null`, `undefined` or `{}`).

0 commit comments

Comments
 (0)