<T>(field: string, formId?: string) => [T, (value: T) => void]
Used for tracking the value of a field in state.
The return signature is the same as the React's own useState
,
but this hook does a few things for you automatically.
- The value is automatically defaulted to the default value of the field.
- The value is globally accessible for a specific form.
- The value can be updated from anywhere.
validate
function will automatically wait for the value to be fully updated before performing the validation.- Automatically resets to default when the form is reset.
Caveats:
- If you call this hook multiple times with the same field name, it will always return the same value, assuming both calls are in the context of the same form.
- If all instances of this hook pointing at a particular field are unmounted, the value will be reset. This mimicks default input behavior
The companion hook useUpdateControlledField
can also be used
to update the value being tracked with this hook.
Controlled Components
This hook can make it easy to integrate controlled components.
Here's an example using a made-up ControlledSelect
component.
import {
useField,
useControlField,
} from "remix-validated-form";
import { ControlledSelect } from 'some-library';
type MySelectProps = {
name: string;
label: string;
options: string[];
};
export const MySelect = ({
name,
label,
options
}: MySelectProps) => {
const { error, getInputProps, validate } = useField(name);
const [value, setValue] = useControlField<string[]>(name);
return (
<div>
{/*
Even though we're tracking the value as an array,
we can still serialize the data
using repeated inputs if we want.
*/}
{value.map(val => (
<input
type="hidden"
name={name}
key={val}
value={val}
/>
))}
<ControlledSelect
label={label}
options={options}
selectedOptions={value}
onSelectionChange={(newSelection) => {
setValue(newSelection);
validate();
}}
/>
{error && (
<span className="my-error-class">{error}</span>
)}
</div>
);
Globally tracking / updating all fields
If you regularly have advanced use-cases that require updating field values,
you may opt to track field state in all fields.
This is as simple as adding useControlField
to all your input components.
Note: This would prevent you from using repeated fields. In this case, you would favor using array syntax instead.
Here's a modified version of MyInput
from the integrate your components page.
import {
useField,
useControlField,
} from "remix-validated-form";
type MyInputProps = {
name: string;
label: string;
};
export const MyInput = ({ name, label }: MyInputProps) => {
const { error, getInputProps } = useField(name);
const [value, setValue] = useControlField(name);
return (
<div>
<label htmlFor={name}>{label}</label>
<input
{...getInputProps({
id: name,
value,
onChange: (e) => setValue(e.target.value),
})}
/>
{error && (
<span className="my-error-class">{error}</span>
)}
</div>
);
};
Occasional field tracking
If you only sometimes need to update the values of a field, this can be used on a case-to-case basis in your form.
export const MyForm = () => {
const [value, setValue] = useControlField(
"myField",
"myForm"
);
useEffect(() => {
// Now we can update the value of the field
// for whatever reason we need.
setValue("Some value");
});
return (
<ValidatedForm validator={myValidator} id="myForm">
<MyInput
name="myField"
// This assumes your input component
// has a `value` and `onChange` prop
value={value}
onChange={(e) => setValue(e.target.value)}
/>
</ValidatedForm>
);
};