Skip to content

Commit 2cc4b09

Browse files
authored
fix: form id ssr error (#160)
* fix: form id ssr error * feat: add custom id field * feat: add multiple form example - Update example navigation style * fix: make uuid outside hook to avoid generating every render * fix: generate uuid in devtool component
1 parent 61ef631 commit 2cc4b09

6 files changed

Lines changed: 117 additions & 66 deletions

File tree

example/src/App.css

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,24 @@ input[type='submit'] {
100100
}
101101

102102
.App {
103-
max-width: 600px;
103+
max-width: 660px;
104104
margin: 0 auto;
105105
height: 100vh;
106106
}
107107

108+
nav {
109+
margin-top: 20px;
110+
}
111+
108112
nav > a {
109113
appearance: none;
110-
margin-top: 40px;
111-
border: 1px solid #333;
112-
margin-bottom: 20px;
114+
text-decoration: inherit;
113115
text-transform: uppercase;
114-
padding: 10px 20px;
116+
color: white;
117+
border: 1px solid white;
115118
border-radius: 4px;
119+
padding: 10px 20px;
120+
margin-inline: 4px;
116121
}
117122

118123
button[type='button'] {

example/src/App.tsx

Lines changed: 20 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,43 @@
1-
import type { PLACEMENT } from '../../src/devTool';
2-
import { DevTool } from '../../src/devTool';
31
import React from 'react';
4-
import { useForm } from 'react-hook-form';
52
import { Link, Route, Routes, useParams } from 'react-router-dom';
3+
import { PLACEMENT } from '../../src/position';
64
import './App.css';
5+
import Form1 from './forms/Form1';
6+
import Form2 from './forms/Form2';
77

8-
const Form: React.FC = () => {
8+
const Page: React.FC<{ multiple?: boolean }> = ({ multiple }) => {
99
const params = useParams();
10-
11-
const { register, control, handleSubmit } = useForm<{
12-
firstName: string;
13-
lastName: string;
14-
custom: string;
15-
ha: {
16-
test: string;
17-
};
18-
}>({
19-
mode: 'onChange',
20-
defaultValues: {
21-
firstName: '',
22-
lastName: '',
23-
ha: {
24-
test: '',
25-
},
26-
},
27-
});
28-
29-
React.useEffect(() => {
30-
register('custom');
31-
}, [register]);
32-
3310
return (
3411
<>
35-
<form onSubmit={handleSubmit((data) => data)}>
36-
<h1>
37-
<span role="img" aria-label="devTool">
38-
🔧
39-
</span>{' '}
40-
DevTools
41-
</h1>
42-
<p style={{ textAlign: 'center' }}>
43-
React Hook Form DevTools to help debug forms.
44-
</p>
45-
<label>First Name</label>
46-
<input {...register('firstName', { required: true })} />
47-
<input {...register('ha.test', { required: true })} />
48-
49-
<label>Last Name</label>
50-
<input {...register('lastName', { required: true })} />
51-
52-
<input style={{ fontWeight: 400 }} type="submit" />
53-
</form>
54-
55-
<DevTool
56-
control={control}
57-
placement={(params?.placement as PLACEMENT) ?? 'top-right'}
58-
/>
12+
<h1>
13+
<span role="img" aria-label="devTool">
14+
🔧
15+
</span>{' '}
16+
DevTools
17+
</h1>
18+
<p style={{ textAlign: 'center' }}>
19+
React Hook Form DevTools to help debug forms.
20+
</p>
21+
<Form1 placement={params?.placement as PLACEMENT} />
22+
{multiple && <Form2 />}
5923
</>
6024
);
6125
};
6226

63-
const App = () => {
27+
const App: React.FC = () => {
6428
return (
6529
<div className="App">
6630
<nav>
6731
<Link to="/">Default</Link>
6832
<Link to="placement/top-left">Top Left</Link>
6933
<Link to="placement/bottom-left">Bottom Left</Link>
7034
<Link to="placement/bottom-right">Bottom Right</Link>
35+
<Link to="multiple">Multiple</Link>
7136
</nav>
7237
<Routes>
73-
<Route path="/" element={<Form />} />
74-
<Route path="placement/:placement" element={<Form />} />
38+
<Route path="/" element={<Page />} />
39+
<Route path="placement/:placement" element={<Page />} />
40+
<Route path="multiple" element={<Page multiple />} />
7541
</Routes>
7642
</div>
7743
);

example/src/forms/Form1.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import { useForm } from 'react-hook-form';
3+
import { DevTool, PLACEMENT } from '../../../src/devTool';
4+
5+
const Form1: React.FC<{ placement: PLACEMENT }> = ({ placement }) => {
6+
const { register, control, handleSubmit } = useForm<{
7+
firstName: string;
8+
lastName: string;
9+
custom: string;
10+
ha: {
11+
test: string;
12+
};
13+
}>({
14+
mode: 'onChange',
15+
defaultValues: {
16+
firstName: '',
17+
lastName: '',
18+
ha: {
19+
test: '',
20+
},
21+
},
22+
});
23+
24+
React.useEffect(() => {
25+
register('custom');
26+
}, [register]);
27+
28+
return (
29+
<>
30+
<form onSubmit={handleSubmit((data) => data)}>
31+
<label>First Name</label>
32+
<input {...register('firstName', { required: true })} />
33+
<input {...register('ha.test', { required: true })} />
34+
35+
<label>Last Name</label>
36+
<input {...register('lastName', { required: true })} />
37+
38+
<input style={{ fontWeight: 400 }} type="submit" />
39+
</form>
40+
41+
<DevTool
42+
id="form1"
43+
control={control}
44+
placement={placement ?? 'top-right'}
45+
/>
46+
</>
47+
);
48+
};
49+
50+
export default Form1;

example/src/forms/Form2.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react';
2+
import { useForm } from 'react-hook-form';
3+
import { DevTool } from '../../../src/devTool';
4+
5+
const Form2: React.FC = () => {
6+
const { control, handleSubmit, register } = useForm({
7+
defaultValues: { lol: '', olo: '' },
8+
});
9+
10+
return (
11+
<>
12+
<form onSubmit={handleSubmit((data) => data)}>
13+
<input {...register('lol', { required: true })} />
14+
<input {...register('olo', { required: true })} />
15+
<input style={{ fontWeight: 400 }} type="submit" />
16+
</form>
17+
<DevTool control={control} />
18+
</>
19+
);
20+
};
21+
22+
export default Form2;

src/devTool.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createStore, StateMachineProvider } from 'little-state-machine';
22
import * as React from 'react';
33
import { Control, FieldValues, useFormContext } from 'react-hook-form';
4+
import { v4 as generateUUID } from 'uuid';
45
import { DevToolUI } from './devToolUI';
56
import { useExportControlToExtension } from './extension/useExportControlToExtension';
67
import type { PLACEMENT } from './position';
@@ -21,14 +22,18 @@ if (typeof window !== 'undefined') {
2122
}
2223

2324
export const DevTool = <T extends FieldValues>(props?: {
25+
id?: string;
2426
control?: Control<T>;
2527
placement?: PLACEMENT;
2628
}) => {
2729
const methods = useFormContext();
2830

29-
const { isExtensionEnabled } = useExportControlToExtension(
30-
props?.control ?? methods.control,
31-
);
31+
const uuid = React.useMemo(() => generateUUID(), []);
32+
33+
const { isExtensionEnabled } = useExportControlToExtension({
34+
id: props?.id ?? uuid,
35+
control: props?.control ?? methods.control,
36+
});
3237
if (isExtensionEnabled) {
3338
return null;
3439
}

src/extension/useExportControlToExtension.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ import _ from 'lodash';
22
import { useEffect, useState } from 'react';
33
import { Control, useFormState, useWatch } from 'react-hook-form';
44
import useDeepCompareEffect from 'use-deep-compare-effect';
5-
import { v4 as uuid } from 'uuid';
65
import { MessageData, UpdatePayload } from './types';
76
import { nestToFlat, proxyToObject } from './utils';
87

9-
const id = uuid();
10-
11-
export function useExportControlToExtension(control: Control<any>) {
8+
export function useExportControlToExtension({
9+
id,
10+
control,
11+
}: {
12+
id: string;
13+
control: Control<any>;
14+
}) {
1215
const nestedFormValues = useWatch({ control });
1316
const formState = useFormState({ control });
1417

0 commit comments

Comments
 (0)