Skip to content

Commit 95c644d

Browse files
committed
useTruthyListener
1 parent 9a384e3 commit 95c644d

File tree

2 files changed

+31
-31
lines changed

2 files changed

+31
-31
lines changed

src/Components.tsx

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React, { useEffect, useRef, useState } from "react";
22
import { ChildFormState, DirtyMap, ErrorMap, FormState } from "./form";
3-
import { useArrayForm, useListener, useAnyListener, useChildForm } from "./hooks";
3+
import { useArrayForm, useListener, useAnyListener, useChildForm, useTruthyListener } from "./hooks";
44

55
/**
66
* Wrapper around useArrayForm (which is a wrapper around useChildForm).
77
* Exports useful functions to manipulate arrays.
8-
* This hook does cause a rerender, but only if the whole array changes.
8+
* This hook does cause a rerender, but only if the whole array becomes null/undefined.
99
* @param parent The parent form.
1010
* @param name The parent's field to create a child form for.
1111
*/
@@ -24,20 +24,9 @@ export function ArrayForm<Parent, ParentState, ParentError, Key extends keyof Pa
2424
}) => React.ReactNode;
2525
}) {
2626
const childForm = useArrayForm(props.form, props.name);
27-
const oldThruthly = useRef(!!props.form.values[props.name]);
28-
const [, setRender] = useState(0);
2927

30-
// Rerender when array became null/not null (thruthly/falsely)
31-
useEffect(() => {
32-
let id = props.form.listen(props.name, () => {
33-
let thruthly = !!props.form.values[props.name];
34-
if (thruthly !== oldThruthly.current) {
35-
setRender((i) => i + 1);
36-
oldThruthly.current = thruthly;
37-
}
38-
});
39-
return () => props.form.ignore(props.name, id);
40-
}, []);
28+
// Causes a rerender when the array becomes null/not null
29+
useTruthyListener(props.form, props.name);
4130

4231
// Do not render anything if the parent field is falsly
4332
if (!props.form.values[props.name]) return null;
@@ -85,7 +74,7 @@ export function AnyListener<T, State, Error>(props: {
8574
/**
8675
* Wrapper around useChildForm
8776
* Creates a child form for another root or child form. You must use this for object and array (see useArrayForm) fields.
88-
* This hook doesn't cause a rerender.
77+
* This hook does cause a rerender, but only if the object field becomes null/undefined.
8978
* @param parentForm The parent form.
9079
* @param name The parent's field to create a child form for.
9180
*/
@@ -95,20 +84,8 @@ export function ChildForm<Parent, ParentState, ParentError, Key extends keyof Pa
9584
render?: (props: ChildFormState<Parent, ParentState, ParentError, Key>) => React.ReactNode;
9685
}) {
9786
const childForm = useChildForm(props.form, props.name);
98-
const oldThruthly = useRef(!!props.form.values[props.name]);
99-
const [, setRender] = useState(0);
100-
101-
// Only rerender when object became null/not null (thruthly/falsely)
102-
useEffect(() => {
103-
let id = props.form.listen(props.name, () => {
104-
let thruthly = !!props.form.values[props.name];
105-
if (thruthly !== oldThruthly.current) {
106-
setRender((i) => i + 1);
107-
oldThruthly.current = thruthly;
108-
}
109-
});
110-
return () => props.form.ignore(props.name, id);
111-
}, []);
87+
// Causes a rerender when the object value becomes null/undefined
88+
useTruthyListener(props.form, props.name);
11289

11390
// Do not render anything if the parent field is falsly
11491
if (!props.form.values[props.name]) return null;

src/hooks.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { DefaultState, DefaultError, FormState, ChildFormState, Validator } from
77
* @param defaultValues The default values for this form.
88
* @param validator The validator to use, optional.
99
* @param validateOnChange Validate on change? Optional, default is false.
10-
* @param validateOnChange Validate on mount? Optional, default is false.
10+
* @param validateOnMount Validate on mount? Optional, default is false.
1111
* @param defaultState The default state for this form. Form state contains custom global states, example: isSubmitting, isLoading ... Optional, default is `{ isSubmitting: false }`.
1212
*/
1313
export function useForm<T, State = DefaultState, Error = DefaultError>(
@@ -187,3 +187,26 @@ export function useArrayForm<Parent, ParentState, ParentError, Key extends keyof
187187
setValues: form.setValues.bind(form)
188188
};
189189
}
190+
191+
/**
192+
* Listen for truthy changes (if a value becomes truthy or falsy) on a form's field. Behaves like useState.
193+
* @param form The form to listen on.
194+
* @param name The form's field to listen to.
195+
*/
196+
export function useTruthyListener<T, State, Error, Key extends keyof T>(form: FormState<T, State, Error>, name: Key) {
197+
const oldTruthy = useRef(!!form.values[name]);
198+
const [, setRender] = useState(0);
199+
200+
useEffect(() => {
201+
let id = form.listen(name, () => {
202+
let thruthly = !!form.values[name];
203+
if (thruthly !== oldTruthy.current) {
204+
setRender((i) => i + 1);
205+
oldTruthy.current = thruthly;
206+
}
207+
});
208+
return () => form.ignore(name, id);
209+
}, []);
210+
211+
return !form.values[name];
212+
}

0 commit comments

Comments
 (0)