|
2 | 2 |
|
3 | 3 | A _convenience_ API for reducing code repetition in a [React](https://react.dev/) application using the [`FormValidityObserver`](../README.md).
|
4 | 4 |
|
5 |
| -Utilities: |
6 |
| - |
7 |
| -- [`createFormValidityObserver`](#function-createformvalidityobservertypes-options) |
8 |
| -- [`useFormValidityObserver`](#custom-hook-useformvalidityobservertypes-options) |
9 |
| - |
10 |
| -Additional Topics: |
11 |
| - |
12 |
| -- [`Usage with Class Components`](#usage-with-class-components) |
13 |
| - |
14 | 5 | ## Function: `createFormValidityObserver(type, options)`
|
15 | 6 |
|
16 | 7 | Creates an enhanced version of the `FormValidityObserver`, known as the `ReactFormValidityObserver`. It accepts the exact same arguments as the [`FormValidityObserver`'s constructor](../README.md#constructor-formvalidityobservertypes-options).
|
17 | 8 |
|
18 |
| -This function acts as the foundation for the [`useFormValidityObserver`](#custom-hook-useformvalidityobservertypes-options) hook. For those using class components, you can use `createFormValidityObserver` directly. |
19 |
| - |
20 | 9 | ### Return Type: `ReactFormValidityObserver<M, R>`
|
21 | 10 |
|
22 | 11 | An enhanced version of the `FormValidityObserver`, designed specifically for React applications. It has the same Type Parameters as the `FormValidityObserver`. As with the `FormValidityObserver`, the type of `M` is derived from the [`renderer`](../README.md#form-validity-observer-options-renderer) option, and the type of `R` is derived from the [`renderByDefault`](../README.md#form-validity-observer-options-render-by-default) option.
|
@@ -220,34 +209,35 @@ function MyForm() {
|
220 | 209 |
|
221 | 210 | The return type of `configure` is simply an object containing the props that should be applied to the configured field. In addition to the field's `name`, this object will include any validation props that were configured by the function (e.g., `required`, `minLength`, `maxLength`, `pattern`, etc.).
|
222 | 211 |
|
223 |
| -## Custom Hook: `useFormValidityObserver(types, options)` |
| 212 | +## Gotchas: Remember to Memoize Your Observer Instance(s) When Necessary |
| 213 | + |
| 214 | +As we mentioned previously, React has a unique re-rendering model. Whenever a state change happens in a React functional component, _the entire component function_ is re-run. If you're instantiating classes (such as the `FormValidityObserver`) in the body of your component's function, then React may re-instantiate the class during a re-render -- even if you don't want that to happen. Sometimes this can lead to inconsistent/unexpected outcomes. |
224 | 215 |
|
225 |
| -A custom React Hook that creates an enhanced version of the `FormValidityObserver` and [memoizes](https://react.dev/reference/react/useMemo) its value. |
| 216 | +To circumvent this problem, React provides the [`useMemo`](https://react.dev/reference/react/useMemo) hook. This hook guarantees that a given value will not change or be recalculated between re-renders. (If you ever want the value to be recalculated, you can provide an array of dependencies that indicate when the value should be recalculated. In the case of the `FormValidityObserver`, we only want to instantiate it _once_, so no dependencies are necessary.) Below is an example of how to use `useMemo` with the `createFormValidityObserver` function. |
226 | 217 |
|
227 | 218 | ```tsx
|
228 | 219 | import { useMemo } from "react";
|
229 |
| -import { useFormValidityObserver } from "@form-observer/react"; |
| 220 | +import { createFormValidityObserver } from "@form-observer/react"; |
230 | 221 |
|
231 | 222 | function MyForm() {
|
232 |
| - const { autoObserve, configure } = useFormValidityObserver("focusout"); |
233 |
| - |
234 |
| - return ( |
235 |
| - // If your component does not re-render, you don't need to memoize `autoObserve`'s return value. |
236 |
| - <form ref={useMemo(autoObserve, [autoObserve])}> |
237 |
| - <input {...configure("first-name", { required: "We need to know who you are!" })} /> |
238 |
| - </form> |
239 |
| - ); |
| 223 | + const { autoObserve, configure } = useMemo(() => createFormValidityObserver("focusout"), []); |
| 224 | + return <form ref={useMemo(autoObserve, [])}>{/* Form Fields */}</form>; |
240 | 225 | }
|
241 | 226 | ```
|
242 | 227 |
|
243 |
| -The purpose of the memoization is two-fold: |
| 228 | +Note: If you are using React's ESLint Rules for hooks, the linter will sometimes tell you to list invalid/unnecessary dependencies for `useMemo`. For example, in the code above, ESLint may tell you to list `autoObserve` as a dependency for the 2nd call to `useMemo`. But because the call to `createFormValidityObserver` is memoized, the `autoObserve` value will never change. Consequently, the 2nd call to `useMemo` should have no dependencies at all. |
| 229 | + |
| 230 | +You can choose to disable the ESLint rule in cases like this one where the rule is incorrect, or you can explicitly list the dependency like so: |
| 231 | + |
| 232 | +```tsx |
| 233 | +<form ref={useMemo(autoObserve, [autoObserve])}>{/* Form Fields */}</form> |
| 234 | +``` |
244 | 235 |
|
245 |
| -1. When the component employing `useFormValidityObserver` re-renders (whether due to state changes or prop updates), the memoization prevents the observer from being re-created/reset. (This is likely not a practical concern if `configure` is only used inside your component's returned JSX.) |
246 |
| -2. Because the outputs of `useFormValidityObserver` are memoized, they won't cause unnecessary re-renders in [memoized children](https://react.dev/reference/react/memo), nor will they cause or unnecessary function re-runs in hooks that depend on them (such as `useEffect`, `useCallback`, and custom hooks that have dependency arrays). |
| 236 | +Since `autoObserve` will never change, the `useMemo` hook will never run any recalculations when `autoObserve` is passed as a dependency. So in the end, you're free to decide how to handle your lint warnings in these situations -- whether by appeasing the linter or by disabling it locally. |
247 | 237 |
|
248 |
| -If you don't need to worry about these scenarios, then you are free to use [`createFormValidityObserver`](#function-createformvalidityobservertypes-options) instead; it will give you the exact same result (unmemoized). If you _do_ need to worry about these scenarios, then bear in mind that **the observer will be recreated whenever the arguments to the hook change, whether by value _or_ by reference**. |
| 238 | +If you know that a functional component using `createFormValidityObserver` will never re-render, then you can ditch the `useMemo` hook entirely. For more details on memoization, see React's documentation on [`useMemo`](https://react.dev/reference/react/useMemo) and [`memo`](https://react.dev/reference/react/memo). You can also read [_When to useMemo and useCallback_](https://kentcdodds.com/blog/usememo-and-usecallback) by Kent C. Dodds. |
249 | 239 |
|
250 |
| -Note that this is a very small hook created solely for your convenience. If you want, you can use `useMemo` directly to wrap any calls to `createFormValidityObserver` instead. For more details on memoization, see React's documentation on [`useMemo`](https://react.dev/reference/react/useMemo) and [`memo`](https://react.dev/reference/react/memo). You can also read [_When to useMemo and useCallback_](https://kentcdodds.com/blog/usememo-and-usecallback) by Kent C. Dodds. |
| 240 | +> Note: For those using class components, "memoization" happens automatically as long as the observer is created only once (i.e., during the class's construction). |
251 | 241 |
|
252 | 242 | ## Usage with Class Components
|
253 | 243 |
|
|
0 commit comments