Skip to content

Commit c424850

Browse files
committed
revert scrollview
1 parent b39c71e commit c424850

File tree

1 file changed

+34
-41
lines changed

1 file changed

+34
-41
lines changed

packages/@react-aria/virtualizer/src/ScrollView.tsx

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,15 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
7373
...otherProps
7474
} = props;
7575

76-
let stateRef = useRef({
76+
let state = useRef({
7777
scrollTop: 0,
7878
scrollLeft: 0,
7979
scrollEndTime: 0,
8080
scrollTimeout: null as ReturnType<typeof setTimeout> | null,
8181
width: 0,
8282
height: 0,
8383
isScrolling: false
84-
});
84+
}).current;
8585
let {direction} = useLocale();
8686

8787
let [isScrolling, setScrolling] = useState(false);
@@ -98,7 +98,6 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
9898
flushSync(() => {
9999
let scrollTop = e.currentTarget.scrollTop;
100100
let scrollLeft = getScrollLeft(e.currentTarget, direction);
101-
let state = stateRef.current;
102101

103102
// Prevent rubber band scrolling from shaking when scrolling out of bounds
104103
state.scrollTop = Math.max(0, Math.min(scrollTop, contentSize.height - state.height));
@@ -139,13 +138,12 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
139138
}, 300);
140139
}
141140
});
142-
}, [props, direction, stateRef, contentSize, onVisibleRectChange, onScrollStart, onScrollEnd]);
141+
}, [props, direction, state, contentSize, onVisibleRectChange, onScrollStart, onScrollEnd]);
143142

144143
// Attach event directly to ref so RAC Virtualizer doesn't need to send props upward.
145144
useEvent(ref, 'scroll', onScroll);
146145

147146
useEffect(() => {
148-
let state = stateRef.current;
149147
return () => {
150148
if (state.scrollTimeout != null) {
151149
clearTimeout(state.scrollTimeout);
@@ -155,7 +153,8 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
155153
window.dispatchEvent(new Event('tk.connect-observer'));
156154
}
157155
};
158-
}, [stateRef]);
156+
// eslint-disable-next-line react-hooks/exhaustive-deps
157+
}, []);
159158

160159
let isUpdatingSize = useRef(false);
161160
let updateSize = useCallback((flush: typeof flushSync) => {
@@ -176,7 +175,6 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
176175
let w = isTestEnv && !isClientWidthMocked ? Infinity : clientWidth;
177176
let h = isTestEnv && !isClientHeightMocked ? Infinity : clientHeight;
178177

179-
let state = stateRef.current;
180178
if (state.width !== w || state.height !== h) {
181179
state.width = w;
182180
state.height = h;
@@ -199,11 +197,36 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
199197
}
200198

201199
isUpdatingSize.current = false;
202-
}, [ref, stateRef, onVisibleRectChange]);
203-
let [update, setUpdate] = useState({});
200+
}, [ref, state, onVisibleRectChange]);
204201
let updateSizeEvent = useEffectEvent(updateSize);
205202

206-
useScrollViewContentSizeChange({updateSize, contentSize, isUpdatingSize, setUpdate});
203+
// Update visible rect when the content size changes, in case scrollbars need to appear or disappear.
204+
let lastContentSize = useRef<Size | null>(null);
205+
let [update, setUpdate] = useState({});
206+
// We only contain a call to setState in here for testing environments.
207+
// eslint-disable-next-line react-hooks/exhaustive-deps
208+
useLayoutEffect(() => {
209+
if (!isUpdatingSize.current && (lastContentSize.current == null || !contentSize.equals(lastContentSize.current))) {
210+
// React doesn't allow flushSync inside effects, so queue a microtask.
211+
// We also need to wait until all refs are set (e.g. when passing a ref down from a parent).
212+
// If we are in an `act` environment, update immediately without a microtask so you don't need
213+
// to mock timers in tests. In this case, the update is synchronous already.
214+
// IS_REACT_ACT_ENVIRONMENT is used by React 18. Previous versions checked for the `jest` global.
215+
// https://github.com/reactwg/react-18/discussions/102
216+
// @ts-ignore
217+
if (typeof IS_REACT_ACT_ENVIRONMENT === 'boolean' ? IS_REACT_ACT_ENVIRONMENT : typeof jest !== 'undefined') {
218+
// This is so we update size in a separate render but within the same act. Needs to be setState instead of refs
219+
// due to strict mode.
220+
setUpdate({});
221+
lastContentSize.current = contentSize;
222+
return;
223+
} else {
224+
queueMicrotask(() => updateSizeEvent(flushSync));
225+
}
226+
}
227+
228+
lastContentSize.current = contentSize;
229+
});
207230

208231
// Will only run in tests, needs to be in separate effect so it is properly run in the next render in strict mode.
209232
useLayoutEffect(() => {
@@ -227,7 +250,7 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
227250
if (scrollDirection === 'horizontal') {
228251
style.overflowX = 'auto';
229252
style.overflowY = 'hidden';
230-
} else if (scrollDirection === 'vertical' || contentSize.width === stateRef.current.width) {
253+
} else if (scrollDirection === 'vertical' || contentSize.width === state.width) {
231254
// Set overflow-x: hidden if content size is equal to the width of the scroll view.
232255
// This prevents horizontal scrollbars from flickering during resizing due to resize observer
233256
// firing slower than the frame rate, which may cause an infinite re-render loop.
@@ -257,33 +280,3 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
257280
}
258281
};
259282
}
260-
261-
function useScrollViewContentSizeChange({updateSize, contentSize, isUpdatingSize, setUpdate}: {updateSize: (flush: typeof flushSync) => void, contentSize: Size, isUpdatingSize: RefObject<boolean>, setUpdate: (update: {}) => void}) {
262-
let updateSizeEvent = useEffectEvent(updateSize);
263-
264-
// Update visible rect when the content size changes, in case scrollbars need to appear or disappear.
265-
let lastContentSize = useRef<Size | null>(null);
266-
// We only contain a call to setState in here for testing environments.
267-
useLayoutEffect(() => {
268-
if (!isUpdatingSize.current && (lastContentSize.current == null || !contentSize.equals(lastContentSize.current))) {
269-
// React doesn't allow flushSync inside effects, so queue a microtask.
270-
// We also need to wait until all refs are set (e.g. when passing a ref down from a parent).
271-
// If we are in an `act` environment, update immediately without a microtask so you don't need
272-
// to mock timers in tests. In this case, the update is synchronous already.
273-
// IS_REACT_ACT_ENVIRONMENT is used by React 18. Previous versions checked for the `jest` global.
274-
// https://github.com/reactwg/react-18/discussions/102
275-
// @ts-ignore
276-
if (typeof IS_REACT_ACT_ENVIRONMENT === 'boolean' ? IS_REACT_ACT_ENVIRONMENT : typeof jest !== 'undefined') {
277-
// This is so we update size in a separate render but within the same act. Needs to be setState instead of refs
278-
// due to strict mode.
279-
setUpdate({});
280-
lastContentSize.current = contentSize;
281-
return;
282-
} else {
283-
queueMicrotask(() => updateSizeEvent(flushSync));
284-
}
285-
}
286-
287-
lastContentSize.current = contentSize;
288-
});
289-
}

0 commit comments

Comments
 (0)