Skip to content

Commit bba5f62

Browse files
authored
fix: resolve scroll key against latest data (#376)
* fix: resolve scroll key against latest data * perf: cache resolved scroll index * refactor: simplify scroll key retry
1 parent 3153dea commit bba5f62

2 files changed

Lines changed: 41 additions & 7 deletions

File tree

src/hooks/useScrollTo.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export default function useScrollTo<T>(
6464
const [syncState, setSyncState] = React.useState<{
6565
times: number;
6666
index: number;
67+
key?: React.Key;
6768
offset: ScrollOffset;
6869
originAlign: ScrollAlign;
6970
targetAlign?: 'top' | 'bottom';
@@ -81,7 +82,11 @@ export default function useScrollTo<T>(
8182

8283
collectHeight();
8384

84-
const { targetAlign, originAlign, index, offset: rawOffset } = syncState;
85+
const { targetAlign, originAlign, offset: rawOffset } = syncState;
86+
const index =
87+
syncState.index >= 0
88+
? syncState.index
89+
: data.findIndex((item) => getKey(item) === syncState.key);
8590
const mergedAlign = targetAlign || originAlign;
8691
const offset = getOffset(rawOffset, { getSize, align: mergedAlign });
8792

@@ -91,7 +96,7 @@ export default function useScrollTo<T>(
9196
let targetTop: number | null = null;
9297

9398
// Go to next frame if height not exist
94-
if (height) {
99+
if (height && index >= 0) {
95100
// Get top & bottom
96101
let stackTop = 0;
97102
let itemTop = 0;
@@ -157,12 +162,13 @@ export default function useScrollTo<T>(
157162

158163
// Trigger next effect
159164
if (needCollectHeight) {
160-
setSyncState({
161-
...syncState,
162-
times: syncState.times + 1,
165+
setSyncState((prev) => ({
166+
...prev,
167+
times: prev.times + 1,
168+
index,
163169
targetAlign: newTargetAlign,
164170
lastTop: targetTop,
165-
});
171+
}));
166172
}
167173
} else if (process.env.NODE_ENV !== 'production' && syncState?.times === MAX_TIMES) {
168174
warning(
@@ -187,19 +193,22 @@ export default function useScrollTo<T>(
187193
syncScrollTop(arg);
188194
} else if (arg && typeof arg === 'object') {
189195
let index: number;
196+
let key: React.Key;
190197
const { align } = arg;
191198

192199
if ('index' in arg) {
193200
({ index } = arg);
194201
} else {
195-
index = data.findIndex((item) => getKey(item) === arg.key);
202+
key = arg.key;
203+
index = data.findIndex((item) => getKey(item) === key);
196204
}
197205

198206
const { offset: rawOffset = 0 } = arg;
199207

200208
setSyncState({
201209
times: 0,
202210
index,
211+
key,
203212
offset: rawOffset,
204213
originAlign: align,
205214
});

tests/scroll.test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,31 @@ describe('List.Scroll', () => {
208208
expect(container.querySelector('ul').scrollTop).toEqual(520);
209209
});
210210

211+
it('refreshes key index when data changes', () => {
212+
const ref = React.createRef();
213+
214+
function Demo() {
215+
const [data, setData] = React.useState(genData(1));
216+
217+
return (
218+
<>
219+
<button
220+
onClick={() => {
221+
setData(genData(100));
222+
ref.current.scrollTo({ key: '30', align: 'top' });
223+
}}
224+
/>
225+
{genNode({ itemHeight: 20, height: 100, data, ref })}
226+
</>
227+
);
228+
}
229+
230+
const { container } = render(<Demo />);
231+
fireEvent.click(container.querySelector('button'));
232+
233+
expect(container.querySelector('ul').scrollTop).toEqual(600);
234+
});
235+
211236
it('supports function offset with getSize info', () => {
212237
const { scrollTo, container } = presetList();
213238
const offset = jest.fn(({ getSize }) => getSize('2').bottom);

0 commit comments

Comments
 (0)