1818
1919<p align =" center " >
2020 <a href =" https://codesandbox.io/p/sandbox/headless-river-3s693c " >CodeSandbox</a > |
21- <a href =" https://github.com/react-hook-form/lenses/tree/main/examples/stories " >Examples</a >
21+ <a href =" https://github.com/react-hook-form/lenses/tree/main/examples/stories " >Examples</a > |
22+ <a href =" https://react-hook-form.com/docs/uselens " >Docs</a >
2223</p >
2324
2425## React Hook Form Lenses
@@ -31,16 +32,6 @@ React Hook Form Lenses is a powerful TypeScript-first library that brings the el
3132npm install @hookform/lenses
3233```
3334
34- ### Features
35-
36- - ** Type-Safe Form State** : Focus on specific parts of your form state with full TypeScript support and precise type inference
37- - ** Functional Lenses** : Build complex form state transformations through composable lens operations
38- - ** Deep Structure Support** : Handle deeply nested structures and arrays elegantly with specialized array operations
39- - ** Seamless Integration** : Work smoothly with React Hook Form's Control API and existing functionality
40- - ** Optimized Performance** : Each lens is cached and reused for optimal performance
41- - ** Array Handling** : Specialized support for array fields with type-safe mapping
42- - ** Composable API** : Build complex form state transformations through lens composition
43-
4435### Quickstart
4536
4637``` tsx
@@ -102,228 +93,3 @@ function StringInput({ lens }: { lens: Lens<string> }) {
10293 return <input { ... lens .interop ((ctrl , name ) => ctrl .register (name ))} />;
10394}
10495```
105-
106- ### API Reference
107-
108- #### Core Types
109-
110- ##### ` Lens<T> `
111-
112- The main lens type that provides operations based on the field type
113-
114- ``` ts
115- type LensWithArray = Lens <string []>;
116- type LensWithObject = Lens <{ name: string ; age: number }>;
117- type LensWithPrimitive = Lens <string >;
118- ```
119-
120- #### Hooks
121-
122- ##### ` useLens `
123-
124- Creates a new lens instance
125-
126- ``` tsx
127- const lens = useLens ({
128- control: form .control , // React Hook Form control
129- });
130- ```
131-
132- You can also pass dependencies to clear lenses with caches and re-create all of them
133-
134- ``` tsx
135- const lens = useLens (
136- {
137- control: form .control , // React Hook Form control
138- },
139- [dependencies ], // optional dependency array if you need to clear caches
140- );
141- ```
142-
143- #### Lens Operations
144-
145- ##### ` focus `
146-
147- Creates a new lens focused on a specific path
148-
149- ``` tsx
150- // Type-safe path focusing
151- const profileLens = lens .focus (' profile' );
152- const emailLens = lens .focus (' profile.contact.email' );
153- const arrayItemLens = lens .focus (' array.0' );
154- ```
155-
156- ##### ` reflect `
157-
158- Transforms the lens structure with type inference.
159- It is useful when you want to create a new lens from existing one with different shape to pass it to a shared component.
160-
161- The first argument is a dictionary of lenses. The second argument is the original lens.
162-
163- ``` tsx
164- const contactLens = lens .reflect (({ profile }) => ({
165- name: profile .focus (' contact.firstName' ),
166- phoneNumber: profile .focus (' contact.phone' ),
167- }));
168-
169- <SharedComponent lens = { contactLens } />;
170-
171- function SharedComponent({ lens }: { lens: Lens <{ name: string ; phoneNumber: string }> }) {
172- // ...
173- }
174- ```
175-
176- ``` tsx
177- const contactLens = lens .reflect ((_ , l ) => ({
178- name: l .focus (' profile.contact.firstName' ),
179- phoneNumber: l .focus (' profile.contact.phone' ),
180- }));
181-
182- <SharedComponent lens = { contactLens } />;
183-
184- function SharedComponent({ lens }: { lens: Lens <{ name: string ; phoneNumber: string }> }) {
185- // ...
186- }
187- ```
188-
189- Also, you can restructure array lens:
190-
191- ``` tsx
192- function ArrayComponent({ lens }: { lens: Lens <{ value: string }[]> }) {
193- return <AnotherComponent lens = { lens .reflect ((_ , l ) => [{ data: l .focus (' value' ) }])} />;
194- }
195-
196- function AnotherComponent({ lens }: { lens: Lens <{ data: string }[]> }) {
197- // ...
198- }
199- ```
200-
201- Pay attention that in case of array reflecting you have to pass an array with single item.
202-
203- In addition you can use ` reflect ` to merge two lenses into one.
204-
205- ``` tsx
206- function Component({ lensA , lensB }: { lensA: Lens <{ firstName: string }>; lensB: Lens <{ lastName: string }> }) {
207- const combined = lensA .reflect ((_ , l ) => ({
208- firstName: l .focus (' firstName' ),
209- lastName: lensB .focus (' lastName' ),
210- }));
211-
212- // ...
213- }
214- ```
215-
216- Keep in mind that is such case the passed to ` reflect ` function is longer pure.
217-
218- You can use spread in reflect if you want to leave other properties as is. In runtime the first argument is just a proxy that calls ` focus ` on the original lens.
219-
220- ``` tsx
221- function Component({ lens }: { lens: Lens <{ firstName: string ; lastName: string ; age: number }> }) {
222- return (
223- <PersonForm
224- lens = { lens .reflect (({ firstName , lastName , ... rest }) => ({
225- ... rest ,
226- name: firstName ,
227- surname: lastName ,
228- }))}
229- />
230- );
231- }
232- ```
233-
234- ##### ` map ` (Array Lenses)
235-
236- Maps over array fields with ` useFieldArray ` integration
237-
238- ``` tsx
239- import { useFieldArray } from ' @hookform/lenses/rhf' ;
240-
241- function ContactsList({ lens }: { lens: Lens <Contact []> }) {
242- const { fields } = useFieldArray (lens .interop ());
243-
244- return lens .map (fields , (value , l ) => <ContactForm key = { value .id } lens = { l } />);
245- }
246- ```
247-
248- ##### ` interop `
249-
250- The ` interop ` method provides integration with react-hook-form by exposing the underlying ` control ` and ` name ` properties. This allows you to connect your lens to react-hook-form's control API.
251-
252- The first variant involves calling ` interop() ` without arguments, which returns an object containing the ` control ` and ` name ` properties for react-hook-form.
253-
254- ``` tsx
255- const { control, name } = lens .interop ();
256-
257- return <input { ... control .register (name )} />;
258- ```
259-
260- The second variant is passing a callback function to ` interop ` which receives the ` control ` and ` name ` properties as arguments. This allows you to work with these properties directly within the callback scope.
261-
262- ``` tsx
263- return (
264- <form onSubmit = { handleSubmit (console .log )} >
265- <input { ... lens .interop ((ctrl , name ) => ctrl .register (name ))} />
266- <input type = " submit" />
267- </form >
268- );
269- ```
270-
271- The ` interop ` method's return value can be passed directly to the ` useController ` hook from react-hook-form, providing seamless integration
272-
273- ``` tsx
274- const { field, fieldState } = useController (lens .interop ());
275-
276- return (
277- <div >
278- <input { ... field } />
279- <p >{ fieldState .error ?.message } </p >
280- </div >
281- );
282- ```
283-
284- ### Caching System
285-
286- All the lenses are cached to prevent component re-renders when utilizing ` React.memo ` .
287- It means that focusing the same path multiple times will not create new lens instance.
288-
289- ``` tsx
290- assert (lens .focus (' firstName' ) === lens .focus (' firstName' ));
291- ```
292-
293- However, there are some difficulties when you use functions, i.e. in ` reflect `
294-
295- ``` tsx
296- lens .reflect ((l ) => l .focus (' firstName' )))
297- ```
298-
299- To make the caching work, you need to memoize the function you pass
300-
301- ``` tsx
302- lens .reflect (useCallback ((l ) => l .focus (' firstName' ), []));
303- ```
304-
305- Here is the case where [ React Compiler] ( https://react.dev/learn/react-compiler ) can be extremely helpful. Because the function you pass to ` reflect ` has no side effects, react compiler will hoist it to module scope and thus lens cache will work as expected.
306-
307- ### Advanced Usage
308-
309- #### Manual Lens Creation
310-
311- You can create lenses manually without ` useLens ` hook by utilizing the ` LensCore ` class:
312-
313- ``` tsx
314- import { useMemo } from ' react' ;
315- import { useForm } from ' react-hook-form' ;
316- import { LensCore , LensesStorage } from ' @hookform/lenses' ;
317-
318- function App() {
319- const { control } = useForm <{ firstName: string ; lastName: string }>();
320-
321- const lens = useMemo (() => {
322- const cache = new LensesStorage (control );
323- return LensCore .create (control , cache );
324- }, [control ]);
325-
326- lens .focus (' firstName' );
327- lens .focus (' lastName' );
328- }
329- ```
0 commit comments