-
Notifications
You must be signed in to change notification settings - Fork 167
perf: skipping re-rendering #190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
6c22ff7
e400d64
ffaa854
0dd37dd
5c2d75b
9225e75
205e410
28fd6b2
462d96d
169288c
48c33b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||||||||||||||||||||||||||||
| import * as React from 'react'; | ||||||||||||||||||||||||||||||||
| import type { RenderFunc, SharedConfig } from '../interface'; | ||||||||||||||||||||||||||||||||
| import type { RenderFunc, GetKey } from '../interface'; | ||||||||||||||||||||||||||||||||
| import { Item } from '../Item'; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export default function useChildren<T>( | ||||||||||||||||||||||||||||||||
|
|
@@ -10,22 +10,25 @@ export default function useChildren<T>( | |||||||||||||||||||||||||||||||
| offsetX: number, | ||||||||||||||||||||||||||||||||
| setNodeRef: (item: T, element: HTMLElement) => void, | ||||||||||||||||||||||||||||||||
| renderFunc: RenderFunc<T>, | ||||||||||||||||||||||||||||||||
| { getKey }: SharedConfig<T>, | ||||||||||||||||||||||||||||||||
| getKey: GetKey<T>, | ||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||
| return list.slice(startIndex, endIndex + 1).map((item, index) => { | ||||||||||||||||||||||||||||||||
| const eleIndex = startIndex + index; | ||||||||||||||||||||||||||||||||
| const node = renderFunc(item, eleIndex, { | ||||||||||||||||||||||||||||||||
| style: { | ||||||||||||||||||||||||||||||||
| width: scrollWidth, | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| offsetX, | ||||||||||||||||||||||||||||||||
| }) as React.ReactElement; | ||||||||||||||||||||||||||||||||
| // The list reference may remain unchanged, but its internal data may change, which can result in different behavior compared to the previous implementation. | ||||||||||||||||||||||||||||||||
| return React.useMemo(() => { | ||||||||||||||||||||||||||||||||
| return list.slice(startIndex, endIndex + 1).map((item, index) => { | ||||||||||||||||||||||||||||||||
| const eleIndex = startIndex + index; | ||||||||||||||||||||||||||||||||
| const node = renderFunc(item, eleIndex, { | ||||||||||||||||||||||||||||||||
| style: { | ||||||||||||||||||||||||||||||||
| width: scrollWidth, | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| offsetX, | ||||||||||||||||||||||||||||||||
| }) as React.ReactElement; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const key = getKey(item); | ||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||
| <Item key={key} setRef={(ele) => setNodeRef(item, ele)}> | ||||||||||||||||||||||||||||||||
| {node} | ||||||||||||||||||||||||||||||||
| </Item> | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
| const key = getKey(item); | ||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||
| <Item key={key} setRef={(ele) => setNodeRef(item, ele)}> | ||||||||||||||||||||||||||||||||
| {node} | ||||||||||||||||||||||||||||||||
| </Item> | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
|
Comment on lines
+26
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainsetRef 闭包与 Item 中 useCallback([]) 组合可能导致使用过期的 setRef 此处每次渲染都会生成新的 针对 export function Item({ children, setRef }: ItemProps) {
- const refFunc = React.useCallback(node => {
- setRef(node);
- }, []);
+ const refFunc = React.useCallback((node) => {
+ setRef(node);
+ }, [setRef]);
return React.cloneElement(children, {
ref: refFunc,
});
}🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo "Listing repo root:"
ls -la || true
# Find Item.tsx and useChildren.tsx files
mapfile -t ITEM_FILES < <(find . -type f -iname '*Item.tsx' 2>/dev/null || true)
mapfile -t USE_FILES < <(find . -type f -iname 'usechildren.tsx' -o -iname '*useChildren.tsx' 2>/dev/null || true)
echo "Found Item files (${#ITEM_FILES[@]}):"
printf '%s\n' "${ITEM_FILES[@]}"
echo "Found useChildren files (${#USE_FILES[@]}):"
printf '%s\n' "${USE_FILES[@]}"
if [ ${#ITEM_FILES[@]} -gt 0 ]; then
for f in "${ITEM_FILES[@]}"; do
echo
echo "==== FILE: $f ===="
sed -n '1,240p' "$f" || true
echo "==== end $f ===="
done
else
echo "No Item.tsx files found."
fi
if [ ${#USE_FILES[@]} -gt 0 ]; then
for f in "${USE_FILES[@]}"; do
echo
echo "==== FILE: $f ===="
sed -n '1,240p' "$f" || true
echo "==== end $f ===="
done
else
echo "No useChildren.tsx files found."
fi
echo
echo "Searching repository for occurrences of 'setRef' and 'setNodeRef' (showing up to 200 matches):"
if command -v rg >/dev/null 2>&1; then
rg -n --hidden --no-ignore -S "setRef" || true
rg -n --hidden --no-ignore -S "setNodeRef" || true
else
grep -RIn --exclude-dir=node_modules --exclude-dir=.git "setRef" . || true
grep -RIn --exclude-dir=node_modules --exclude-dir=.git "setNodeRef" . || true
fi
echo
echo "If Item.tsx was found, searching for useCallback usage inside it:"
for f in "${ITEM_FILES[@]}"; do
echo "---- $f ----"
if command -v rg >/dev/null 2>&1; then
rg -n "useCallback" "$f" || true
rg -n "useCallback\(" "$f" || true
rg -n "useCallback\([^)]*\)\s*,\s*\[\s*\]" "$f" || true
else
grep -n "useCallback" "$f" || true
fi
done
echo
echo "Done."Length of output: 1392 重新运行脚本以定位并打印 src/Item.tsx 与 src/hooks/useChildren.tsx 内容,并搜索 setRef / setNodeRef / useCallback 使用位置: 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo "Listing src top-level:"
ls -la src || true
# Check a few likely exact paths first
candidates=(
"src/Item.tsx"
"src/Item.jsx"
"src/components/Item.tsx"
"src/components/Item.jsx"
"src/hooks/Item.tsx"
"src/hooks/useChildren.tsx"
"src/hooks/usechildren.tsx"
"src/hooks/useChildren.jsx"
"src/hooks/useChildren.ts"
)
for p in "${candidates[@]}"; do
if [ -f "$p" ]; then
echo
echo "==== FOUND: $p ===="
sed -n '1,240p' "$p" || true
echo "==== end $p ===="
fi
done
echo
echo "Finding any *Item.* and *useChildren.* under src (paths):"
find src -type f \( -iname '*Item.*' -o -iname '*useChildren.*' -o -iname '*usechildren.*' \) -print || true
echo
echo "Printing contents of any matching Item.tsx / useChildren.tsx (first 240 lines):"
find src -type f \( -iname '*Item.tsx' -o -iname '*useChildren.tsx' -o -iname '*usechildren.tsx' \) -print0 2>/dev/null | while IFS= read -r -d '' file; do
echo
echo "---- $file ----"
sed -n '1,240p' "$file" || true
echo "---- end $file ----"
done
echo
echo "Searching repository (src/) for usages of 'setRef', 'setNodeRef' and 'useCallback':"
if command -v rg >/dev/null 2>&1; then
rg -n --hidden --no-ignore -S "setRef" src || true
rg -n --hidden --no-ignore -S "setNodeRef" src || true
rg -n --hidden --no-ignore -S "useCallback" src || true
else
grep -RIn --exclude-dir=node_modules --exclude-dir=.git "setRef" src || true
grep -RIn --exclude-dir=node_modules --exclude-dir=.git "setNodeRef" src || true
grep -RIn --exclude-dir=node_modules --exclude-dir=.git "useCallback" src || true
fi
echo
echo "Done."Length of output: 5261 修复:Item.tsx 的 ref 回调捕获过期的 setRef,需将 useCallback 依赖改为 [setRef] Item 中 refFunc 的 useCallback 使用空依赖,会固定首次传入的 setRef;useChildren 向 Item 传入的 setRef 为内联函数 位置:src/Item.tsx export function Item({ children, setRef }: ItemProps) {
- const refFunc = React.useCallback(node => {
- setRef(node);
- }, []);
+ const refFunc = React.useCallback((node) => {
+ setRef(node);
+ }, [setRef]);
return React.cloneElement(children, {
ref: refFunc,
});
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
| }, [list, startIndex, endIndex, setNodeRef, renderFunc, getKey, offsetX, scrollWidth]); | ||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 稍微有点复杂了,未来如果加 props 也容易 break。如果是只是为了 Item 不需要重新渲染,可以考虑 Item 直接用 React.memo 包一下,条件里直接忽略 setRef 即可。 |
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -69,7 +69,7 @@ export default function useHeights<T>( | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| function setInstanceRef(item: T, instance: HTMLElement) { | ||||||
| const setInstanceRef = React.useCallback(function setInstanceRef(item: T, instance: HTMLElement) { | ||||||
| const key = getKey(item); | ||||||
| const origin = instanceRef.current.get(key); | ||||||
|
|
||||||
|
|
@@ -88,11 +88,12 @@ export default function useHeights<T>( | |||||
| onItemRemove?.(item); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| }, [getKey, onItemAdd, onItemRemove]); | ||||||
|
|
||||||
| useEffect(() => { | ||||||
| return cancelRaf; | ||||||
| }, []); | ||||||
|
||||||
| }, []); | |
| }, [cancelRaf]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
useChildren 调用已改为传入 getKey;请检查仓库内是否仍有 SharedConfig 遗留引用
为避免类型/行为不一致,请全局确认:
SharedConfiguseChildren调用点参数顺序已更新🏁 Script executed:
Length of output: 600
修复:为 src/Item.tsx 中的 useCallback 添加 setRef 依赖;同时全局确认 SharedConfig/useChildren 变更
const refFunc = React.useCallback(node => { setRef(node); }, []);
缺少 setRef 依赖。修复为将依赖数组改为 [setRef](或确保 setRef 为稳定引用)。位置:src/Item.tsx(refFunc 定义处)。
rg -nP --glob '!/node_modules/' '\bSharedConfig<'
rg -nP --glob '!/node_modules/' 'useChildren\s*(' -C2
🤖 Prompt for AI Agents