Skip to content

Commit 8e9f6be

Browse files
committed
innerRef prop on Field/FieldError
1 parent 90294cb commit 8e9f6be

File tree

4 files changed

+34
-9
lines changed

4 files changed

+34
-9
lines changed

Todo.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
- Nested validators in child forms to improve validation performance
33
- Combine array helpers into one object? This is usefull to pass to other components
44
- Require index for array fields
5-
- Add React.forwardRef to input elements
6-
- Let `comparePrimitiveObject` compare deep objects too
75
- Field on blur
6+
- Use React.forwardRef instead of innerRef on Field/FieldError

src/Field.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export type FieldProps<T extends object, K extends keyof T, C> = {
1414
*/
1515
form: FormState<T>;
1616
/**
17-
* The name of the field
17+
* The name of the field in form
1818
*/
1919
name: K;
2020
/**
@@ -59,11 +59,17 @@ export type FieldProps<T extends object, K extends keyof T, C> = {
5959
* The style to set when this field has been modified.
6060
*/
6161
dirtyStyle?: React.CSSProperties;
62+
/**
63+
* The ref to pass to your input component.
64+
*/
65+
innerRef?: React.Ref<any>;
6266
};
6367

68+
// Note on innerRef: React.forwardRef breaks type-checking
69+
6470
export function Field<T extends object, K extends keyof T, C extends React.FunctionComponent<any> | keyof JSX.IntrinsicElements = "input">(
6571
props: FieldProps<T, K, C> &
66-
Omit<ElementProps<C>, "value" | "checked" | "onChange" | "field" | keyof FieldProps<T, K, C> | keyof SerializeProps> &
72+
Omit<ElementProps<C>, "value" | "checked" | "onChange" | "field" | "ref" | keyof FieldProps<T, K, C> | keyof SerializeProps> &
6773
SerializeProps<T[K]>
6874
) {
6975
const {
@@ -82,6 +88,7 @@ export function Field<T extends object, K extends keyof T, C extends React.Funct
8288
errorStyle,
8389
dirtyClassName,
8490
dirtyStyle,
91+
innerRef,
8592
...rest
8693
} = props;
8794
const serializeProps = {
@@ -96,6 +103,7 @@ export function Field<T extends object, K extends keyof T, C extends React.Funct
96103
let v = (serializer ?? defaultSerializer)(field.value, serializeProps);
97104
return React.createElement(as, {
98105
...rest,
106+
ref: innerRef,
99107
checked: typeof v === "boolean" ? v : undefined,
100108
value: typeof v === "boolean" ? undefined : v,
101109
disabled: field.state.isSubmitting || disabled,
@@ -104,7 +112,11 @@ export function Field<T extends object, K extends keyof T, C extends React.Funct
104112
field,
105113
onChange: (ev: any) => {
106114
let v =
107-
typeof ev === "object" && "target" in ev ? (["checkbox", "radio"].includes(props.type!) ? ev.target.checked : ev.target.value) : ev;
115+
typeof ev === "object" && "target" in ev
116+
? props.type === "checkbox" || props.type === "radio"
117+
? ev.target.checked
118+
: ev.target.value
119+
: ev;
108120
if (typeof v === "string" || typeof v === "boolean")
109121
field.setValue((deserializer ?? defaultDeserializer)(v, field.value, serializeProps));
110122
else field.setValue(v);

src/FieldError.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ export function FieldError<
3030
*/
3131
as?: C;
3232
transformer?: (error: ErrorType<T[K], Error>) => React.ReactNode;
33-
} & Omit<ElementProps<C>, "transformer" | "as" | "name" | "form" | "children" | "field">
33+
/**
34+
* The ref to pass to your error component.
35+
*/
36+
innerRef?: React.Ref<any>;
37+
} & Omit<ElementProps<C>, "transformer" | "as" | "name" | "form" | "children" | "field" | "ref">
3438
) {
35-
const { form, as = React.Fragment, transformer, ...rest } = props;
39+
const { form, as = React.Fragment, transformer, innerRef, ...rest } = props;
3640
const field = useListener(form, props.name);
3741
if (!field.error || typeof field.error === "object") return null;
38-
return React.createElement(as, { ...rest, field, children: transformer ? transformer(field.error) : String(field.error) });
42+
return React.createElement(as, { ...rest, ref: innerRef, field, children: transformer ? transformer(field.error) : String(field.error) });
3943
}

testing/src/Fieldform.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { useRef } from "react";
22
import { useForm, Field, AnyListener } from "typed-react-form";
33

44
const inputStyle: React.CSSProperties = {
@@ -13,6 +13,7 @@ function CustomInput(props: { value: string; onChange: (val: string) => void; my
1313

1414
export function FieldForm() {
1515
const form = useForm({ firstName: "", lastName: "" });
16+
const inputRef = useRef<HTMLInputElement>(null);
1617

1718
function submit() {
1819
console.log(form.values);
@@ -22,6 +23,7 @@ export function FieldForm() {
2223
<form onSubmit={form.handleSubmit(submit)}>
2324
<Field form={form} name="firstName" as={CustomInput} myCustomProp={true} />
2425
<Field form={form} name="lastName" as={CustomInput} />
26+
<Field form={form} name="firstName" innerRef={inputRef} />
2527
<button type="submit">Go</button>
2628
<button
2729
type="button"
@@ -32,6 +34,14 @@ export function FieldForm() {
3234
Set values
3335
</button>
3436
<AnyListener form={form} render={({ dirty }) => <pre>{String(dirty)}</pre>} />
37+
<button
38+
type="button"
39+
onClick={() => {
40+
inputRef.current!.style.color = Math.random() > 0.5 ? "red" : "blue";
41+
}}
42+
>
43+
Test
44+
</button>
3545
</form>
3646
);
3747
}

0 commit comments

Comments
 (0)