Skip to content

Commit 59b9c85

Browse files
committed
improve the fix
1 parent 9efe706 commit 59b9c85

File tree

3 files changed

+34
-30
lines changed

3 files changed

+34
-30
lines changed

example/index.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,20 @@ const WithCustomFont = () => {
203203
};
204204

205205
const WithFormReset = () => {
206+
const ref = React.useRef<HTMLFormElement>(null);
207+
return (
208+
<div>
209+
<h2>{'Resettable form.'}</h2>
210+
<div>{'Resizes once the form gets reset.'}</div>
211+
<form ref={ref}>
212+
<TextareaAutosize />
213+
<input type="reset" />
214+
</form>
215+
</div>
216+
);
217+
};
218+
219+
const WithManualFormReset = () => {
206220
const ref = React.useRef<HTMLFormElement>(null);
207221
return (
208222
<div>
@@ -232,6 +246,7 @@ const Demo = () => {
232246
<MultipleTextareas />
233247
<WithCustomFont />
234248
<WithFormReset />
249+
<WithManualFormReset />
235250
</div>
236251
);
237252
};

src/hooks.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,32 +25,34 @@ type InferEvent<
2525
: Event;
2626

2727
function useListener<
28-
TTarget extends EventTarget | null | undefined,
29-
TType extends InferEventType<NonNullable<TTarget>>,
28+
TTarget extends EventTarget,
29+
TType extends InferEventType<TTarget>,
3030
>(
31-
target: (() => TTarget) | TTarget,
31+
target: TTarget,
3232
type: TType,
33-
listener: (event: InferEvent<NonNullable<TTarget>, TType>) => void,
33+
listener: (event: InferEvent<TTarget, TType>) => void,
3434
) {
3535
const latestListener = useLatest(listener);
3636
React.useLayoutEffect(() => {
3737
const handler: typeof listener = (ev) => latestListener.current(ev);
38-
const resolvedTarget = typeof target === 'function' ? target() : target;
39-
4038
// might happen if document.fonts is not defined, for instance
41-
if (!resolvedTarget) {
39+
if (!target) {
4240
return;
4341
}
44-
resolvedTarget.addEventListener(type, handler);
45-
return () => resolvedTarget.removeEventListener(type, handler);
42+
target.addEventListener(type, handler);
43+
return () => target.removeEventListener(type, handler);
4644
}, []);
4745
}
4846

4947
export const useFormResetListener = (
5048
libRef: React.MutableRefObject<HTMLTextAreaElement | null>,
5149
listener: (event: Event) => any,
5250
) => {
53-
useListener(() => libRef.current?.form, 'reset', listener);
51+
useListener(document.body, 'reset', (ev) => {
52+
if (libRef.current?.form === ev.target) {
53+
listener(ev);
54+
}
55+
});
5456
};
5557

5658
export const useWindowResizeListener = (listener: (event: UIEvent) => any) => {
@@ -60,8 +62,3 @@ export const useWindowResizeListener = (listener: (event: UIEvent) => any) => {
6062
export const useFontsLoadedListener = (listener: (event: Event) => any) => {
6163
useListener(document.fonts, 'loadingdone', listener);
6264
};
63-
64-
export const useForceRerender = () => {
65-
const [, setState] = React.useState({});
66-
return React.useCallback(() => setState({}), []);
67-
};

src/index.tsx

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
useWindowResizeListener,
99
useFontsLoadedListener,
1010
useFormResetListener,
11-
useForceRerender,
1211
} from './hooks';
1312
import { noop } from './utils';
1413

@@ -99,23 +98,16 @@ const TextareaAutosize: React.ForwardRefRenderFunction<
9998
};
10099

101100
if (isBrowser) {
102-
const forceRerender = useForceRerender();
103101
React.useLayoutEffect(resizeTextarea);
104102
useFormResetListener(libRef, () => {
105103
if (!isControlled) {
106-
// force rerender is used here because form reset doesn't trigger React's onChange:
107-
// https://github.com/facebook/react/issues/19078
108-
//
109-
// the problem with a reset listener is that it's called before the value gets actually changed
110-
// the event itself can, after all, be even .preventDefault()ed
111-
// so given it's not possible to know if the reset will actually happen, we "schedule" a rerender so our resizing layout effect can take care of it
112-
//
113-
// this doesn't work with <input type="reset" /> though
114-
// updates scheduled by reset handlers called called by those happen synchronously~
115-
// React is eager to rerender this before the reset action actually takes place
116-
//
117-
// it might be a good idea to use a native change listener on the textarea itself to workaround this
118-
forceRerender();
104+
const node = libRef.current!;
105+
const currentValue = node.value;
106+
requestAnimationFrame(() => {
107+
if (currentValue !== node.value) {
108+
resizeTextarea();
109+
}
110+
});
119111
}
120112
});
121113
useWindowResizeListener(resizeTextarea);

0 commit comments

Comments
 (0)