Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions src/hooks/useScrollTo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export default function useScrollTo<T>(
const [syncState, setSyncState] = React.useState<{
times: number;
index: number;
key?: React.Key;
offset: ScrollOffset;
originAlign: ScrollAlign;
targetAlign?: 'top' | 'bottom';
Expand All @@ -81,7 +82,11 @@ export default function useScrollTo<T>(

collectHeight();

const { targetAlign, originAlign, index, offset: rawOffset } = syncState;
const { targetAlign, originAlign, offset: rawOffset } = syncState;
const index =
syncState.index >= 0
? syncState.index
: data.findIndex((item) => getKey(item) === syncState.key);
Comment on lines +85 to +89

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

请让同步 effect 在 data 变化时重新运行。

index 的解析依赖当前 data,但该 effect 的依赖数组没有 data。如果先调用 scrollTo({ key: '30' }),目标尚不存在,第一次 effect 会保存 index = -1 并结束。之后列表在另一次 render 中加入目标项时,syncState 和容器引用都不变,effect 不会再次执行,滚动请求会永久停留在 -1。请将 data 加入依赖数组,并补充跨两个 render 的回归测试。

建议修改
-  }, [syncState, containerRef.current]);
+  }, [syncState, data, containerRef.current]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useScrollTo.tsx` around lines 85 - 89, 让 useScrollTo 中解析
syncState.index 的同步 effect 将 data 加入依赖数组,使数据更新后重新查找目标并执行滚动。保持现有 syncState
与容器引用依赖不变,并为目标跨两个 render 才出现在 data 中的场景补充回归测试。

const mergedAlign = targetAlign || originAlign;
const offset = getOffset(rawOffset, { getSize, align: mergedAlign });

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

// Go to next frame if height not exist
if (height) {
if (height && index >= 0) {
// Get top & bottom
let stackTop = 0;
let itemTop = 0;
Expand Down Expand Up @@ -157,12 +162,13 @@ export default function useScrollTo<T>(

// Trigger next effect
if (needCollectHeight) {
setSyncState({
...syncState,
times: syncState.times + 1,
setSyncState((prev) => ({
...prev,
times: prev.times + 1,
index,
targetAlign: newTargetAlign,
lastTop: targetTop,
});
}));
}
} else if (process.env.NODE_ENV !== 'production' && syncState?.times === MAX_TIMES) {
warning(
Expand All @@ -187,19 +193,22 @@ export default function useScrollTo<T>(
syncScrollTop(arg);
} else if (arg && typeof arg === 'object') {
let index: number;
let key: React.Key;
const { align } = arg;

if ('index' in arg) {
({ index } = arg);
} else {
index = data.findIndex((item) => getKey(item) === arg.key);
key = arg.key;
index = data.findIndex((item) => getKey(item) === key);
}

const { offset: rawOffset = 0 } = arg;

setSyncState({
times: 0,
index,
key,
offset: rawOffset,
originAlign: align,
});
Expand Down
25 changes: 25 additions & 0 deletions tests/scroll.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,31 @@ describe('List.Scroll', () => {
expect(container.querySelector('ul').scrollTop).toEqual(520);
});

it('refreshes key index when data changes', () => {
const ref = React.createRef();

function Demo() {
const [data, setData] = React.useState(genData(1));

return (
<>
<button
onClick={() => {
setData(genData(100));
ref.current.scrollTo({ key: '30', align: 'top' });
}}
/>
{genNode({ itemHeight: 20, height: 100, data, ref })}
</>
);
}

const { container } = render(<Demo />);
fireEvent.click(container.querySelector('button'));

expect(container.querySelector('ul').scrollTop).toEqual(600);
});

it('supports function offset with getSize info', () => {
const { scrollTo, container } = presetList();
const offset = jest.fn(({ getSize }) => getSize('2').bottom);
Expand Down
Loading