Skip to content

Commit 78bb40c

Browse files
committed
Pass form to Field child
1 parent a265baa commit 78bb40c

File tree

5 files changed

+38
-22
lines changed

5 files changed

+38
-22
lines changed

Todo.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
- Rename `setDefaultValues` to `setAllValues`
1010
- Let `comparePrimitiveObject` compare deep objects too
1111
- Render JSON.stringify by default when using AnyListener without renderer
12+
- Field on blur

src/Components.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function ArrayField<
4545
* @param form The form to listen on.
4646
* @param name The form's field to listen to.
4747
*/
48-
export function Listener<T extends object, K extends keyof T, State = DefaultState, Error extends string = DefaultError>(props: {
48+
export function Listener<T extends object, K extends keyof T, State extends DefaultState = DefaultState, Error extends string = DefaultError>(props: {
4949
form: FormState<T, State, Error>;
5050
name: K;
5151
render?: (props: {

src/Field.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FormState } from "./form";
2-
import React, { useCallback } from "react";
2+
import React from "react";
33
import { useListener } from "./hooks";
44

55
export type ElementProps<C extends React.FunctionComponent<any> | keyof JSX.IntrinsicElements> = C extends React.FunctionComponent<infer P>
@@ -46,7 +46,9 @@ export type FieldProps<T extends object, K extends keyof T, C> = {
4646
};
4747

4848
export function Field<T extends object, K extends keyof T, C extends React.FunctionComponent<any> | keyof JSX.IntrinsicElements = "input">(
49-
props: FieldProps<T, K, C> & Omit<ElementProps<C>, "value" | "onChange" | keyof FieldProps<T, K, C> | keyof SerializeProps> & SerializeProps<T[K]>
49+
props: FieldProps<T, K, C> &
50+
Omit<ElementProps<C>, "value" | "checked" | "onChange" | "field" | keyof FieldProps<T, K, C> | keyof SerializeProps> &
51+
SerializeProps<T[K]>
5052
) {
5153
const { form, as = "input", serializer, dateAsNumber, setNullOnUncheck, setUndefinedOnUncheck, deserializer, hideWhenNull, ...rest } = props;
5254
const serializeProps = {
@@ -56,23 +58,21 @@ export function Field<T extends object, K extends keyof T, C extends React.Funct
5658
type: props.type,
5759
value: props.value
5860
};
59-
const { value, setValue, state } = useListener(form, props.name);
60-
const onChange = useCallback(
61-
(ev: any) => {
62-
let v = "target" in ev ? (["checkbox", "radio"].includes(props.type!) ? ev.target.checked : ev.target.value) : ev;
63-
if (typeof v === "string" || typeof v === "boolean") setValue((deserializer ?? defaultDeserializer)(v, value, serializeProps));
64-
else setValue(v);
65-
},
66-
[setValue, props.type]
67-
);
68-
if (hideWhenNull && (value === undefined || value === null)) return null;
69-
let v = (serializer ?? defaultSerializer)(value, serializeProps);
61+
const field = useListener(form, props.name);
62+
if (hideWhenNull && (field.value === undefined || field.value === null)) return null;
63+
let v = (serializer ?? defaultSerializer)(field.value, serializeProps);
7064
return React.createElement(as, {
7165
...rest,
7266
checked: typeof v === "boolean" ? v : undefined,
7367
value: typeof v === "boolean" ? undefined : v,
74-
disabled: state.isSubmitting,
75-
onChange
68+
disabled: field.state.isSubmitting || props.disabled,
69+
field,
70+
onChange: (ev: any) => {
71+
let v = "target" in ev ? (["checkbox", "radio"].includes(props.type!) ? ev.target.checked : ev.target.value) : ev;
72+
if (typeof v === "string" || typeof v === "boolean")
73+
field.setValue((deserializer ?? defaultDeserializer)(v, field.value, serializeProps));
74+
else field.setValue(v);
75+
}
7676
});
7777
}
7878

src/FieldError.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ export function FieldError<
77
T extends object,
88
K extends keyof T,
99
C extends React.FunctionComponent<any> | keyof JSX.IntrinsicElements,
10-
Error extends string = DefaultError,
11-
State = DefaultState
10+
Error extends DefaultError = DefaultError,
11+
State extends DefaultState = DefaultState
1212
>(
1313
props: {
1414
/**

src/hooks.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useRef, useEffect, useState, useCallback } from "react";
2-
import { DefaultState, DefaultError, FormState, ChildFormState, Validator, FieldsOfType, KeysOfType } from "./form";
2+
import { DefaultState, DefaultError, FormState, ChildFormState, Validator, FieldsOfType, KeysOfType, ErrorType } from "./form";
33

44
/**
55
* Creates a new root form.
@@ -71,16 +71,31 @@ export function useObjectField<
7171
return c.current;
7272
}
7373

74+
export interface FormField<
75+
T extends object = any,
76+
K extends keyof T = never,
77+
State extends DefaultState = DefaultState,
78+
Error extends string = DefaultError
79+
> {
80+
value: T[K];
81+
defaultValue: T[K];
82+
setValue: (value: T[K]) => void;
83+
dirty: boolean;
84+
error: ErrorType<T[K], Error> | undefined;
85+
state: State;
86+
form: FormState<T, State, Error>;
87+
}
88+
7489
/**
7590
* Listen for changes on a form's field. Behaves like useState.
7691
* You shouldn't use this hook in large components, as it rerenders each time something changes. Use the wrapper <Listener /> instead.
7792
* @param form The form to listen on.
7893
* @param name The form's field to listen to.
7994
*/
80-
export function useListener<T extends object, K extends keyof T, State = DefaultState, Error extends string = DefaultError>(
95+
export function useListener<T extends object, K extends keyof T, State extends DefaultState = DefaultState, Error extends string = DefaultError>(
8196
form: FormState<T, State, Error>,
8297
name: K
83-
) {
98+
): FormField<T, K, State, Error> {
8499
const [, setRender] = useState(0);
85100

86101
useEffect(() => {
@@ -94,7 +109,7 @@ export function useListener<T extends object, K extends keyof T, State = Default
94109
value: form.values[name],
95110
defaultValue: form.defaultValues[name],
96111
setValue: (value: T[K]) => form.setValue(name, value),
97-
dirty: form.dirtyMap[name],
112+
dirty: form.dirtyMap[name] ?? false,
98113
error: form.errorMap[name],
99114
state: form.state,
100115
form

0 commit comments

Comments
 (0)