---
tags: zeta-dom-react
---
# Data object and array
==Since `v0.4.0`==
Nested data objects and arrays are supported since v0.4.
Field components can be associated with nested data properties using `FormObject` and `FormArray` component.
```typescript
interface FormData {
name: string;
phone: {
countryCode: string;
phoneNumber: string;
};
}
function Form() {
const form = useFormContext<FormData>();
return (
<Form context={form}>
<TextField name="name" />
<FormObject name="phone">
<TextField name="countryCode" />
<TextField name="phoneNumber" />
</FormObject>
</Form>
);
}
```
## Field component for data objects
```typescript
interface PhoneData {
countryCode: string;
phoneNumber: string;
}
function PhoneField(props: FormFieldProps<PhoneData>) {
const { value } = useFormField(props, {});
return (
<div>
<FormObject value={value}>
<TextField name="countryCode" />
<TextField name="phoneNumber" />
</FormObject>
</div>
);
}
function Form() {
const form = useFormContext<FormData>();
return (
<Form context={form}>
<TextField name="name" />
<PhoneField name="phone" />
</Form>
);
}
```
## Data array
```typescript
interface Item {
id: string;
name: string;
}
interface FormData {
items: Item[];
}
function Form() {
const form = useFormContext<FormData>();
return (
<Form context={form}>
<FormArray name="items">
{items => item.map(v => (
// important to provide key for each mapped element
<FormObject key={FormObject.keyFor(v)} value={v}>
<div>
<TextField name="id" />
<TextField name="name" />
</div>
</FormObject>
))}
</FormArray>
</Form>
);
}
```
Or by having a composite field component simply:
```typescript
function Form() {
const form = useFormContext<FormData>();
return (
<Form context={form}>
<FormArray name="items">
{items => item.map((v, i) => (
<Field key={FormObject.keyFor(v)} name={i} />
))}
</FormArray>
</Form>
);
}
```
## Data path
Events and operation like validations on nested data properties can be achieved like normal data properties with dot-separated data paths.
```typescript
function Form() {
const form = useFormContext<FormData>();
useEffect(() => {
return form.on('dataChange', function (e) {
console.log(e.data);
});
}, [form]);
return (
<Form context={form}>
<TextField name="name" />
<FormObject name="phone">
<TextField name="countryCode" />
<TextField name="phoneNumber" />
</FormObject>
</Form>
);
}
```
For instance, the above `dataChange` event handler will log `["phone.countryCode"]` when user fills in the country code.
### Validate nested properties
To validate a particular data property, provide the data path to that property like:
```typescript
form.validate('phone.countryCode')
```
You can also supply the root object data path to validate all child properties:
```typescript
form.validate('phone')
// same as
form.validate('phone.countryCode', 'phone.phoneNumber')
```
### Access data with data path
Other than accessing like ordinary JavaScript objects, `FormContext` provides `getValue` and `setValue` method to access values providing a data path.
```typescript
const countryCode = form.getValue('phone.countryCode');
form.setValue('phone.countryCode', '1');
```
:::warning
Note that for data objects and array, `getValue` will return a deeply-cloned object; while `setValue` will deeply clone the supplied object.
:::