我制作了一个多选列表,并一直在努力提高其重新渲染性能。下面的代码显示了我所做的(在我的实际项目中,列表要长得多)。目前,当我单击列表项时,它会重新呈现每一行。我只想渲染单击的行。看起来 React.memo、React.useCallback 是可能的,但我还没有成功地在这个用例中正确使用它们。欢迎任何反馈!提前致谢。
<div id="app" />
Run Code Online (Sandbox Code Playgroud)
const animals = ['Cat', 'Dog', 'Bird', 'Fish']
interface AnimalProps {
selected: boolean;
name: string;
onClick: (name: string) => void;
}
const Animal = ({selected, name, onClick}: AnimalProps) => (
<div onClick={onClick}>{`[${selected ? 'X' : ' '}]`} {name}</div>
)
const useFilter = (initialValue: string[]) => {
const [items, setItems] = React.useState<string[]>(initialValue)
const makeOnClick = (item: string) => () => {
const ix = items.indexOf(item)
const newItems = (ix > -1) ?
[...items.slice(0, ix), ...items.slice(ix + 1)]
:
[...items, item]
setItems(newItems)
}
const isSelected = (item: string) => items.indexOf(item) > -1
return { items, makeOnClick, isSelected };
}
const App = () => {
const {items, makeOnClick, isSelected} = useFilter([])
return (
<div>
<ul>
{animals.map(i => (
<Animal name={i} selected={isSelected(i)} onClick={makeOnClick(i)} key={i} />
))}
</ul>
<div>Selected: {items.join(', ')}</div>
</div>
)
}
ReactDOM.render(<App />, document.querySelector('#app'))
Run Code Online (Sandbox Code Playgroud)
"react": "17.0.2",
"react-dom": "17.0.2",
Run Code Online (Sandbox Code Playgroud)
首先,我们已经知道我们的目标是防止 Animal 进行不必要的渲染。让我们添加React.memo:
const Animal = React.memo(({selected, name, onClick}: AnimalProps) => (
<div onClick={onClick}>{`[${selected ? 'X' : ' '}]`} {name}</div>
))
Run Code Online (Sandbox Code Playgroud)
当然现在它什么也没做。我们可以清楚地看到name和selected是静态值。它们只是字符串和布尔值,因此它们应该能够React.memo很好地配合使用。问题是onClick.
在此代码中,我们可以看到我们正在循环中创建新函数:
const makeOnClick = (item: string) => () => {
// ...
}
makeOnClick(i)
Run Code Online (Sandbox Code Playgroud)
在每一次渲染中,生成的函数始终是不同的。当前的代码类似于:
<Animal onClick={() => onClick(i)} />
Run Code Online (Sandbox Code Playgroud)
这永远不会与 React.memo 一起使用,因为它总是创建一个新函数。所以我们的目标是向它传递一个静态函数。我们只需将其修改为:
<Animal onClick={() => onClick(i)} />
Run Code Online (Sandbox Code Playgroud)
但我们仍然需要了解i。所以在 Animal 组件中,我们可以这样做:
<Animal onClick={onClick} />
Run Code Online (Sandbox Code Playgroud)
在这种情况下,如果onClick是静态值,那么 Animal 组件将不会进行不必要的渲染。所以我们的最终目标是为 Animal 创建一个静态的 onClick 函数。
而且makeOnClick不再是makeOnClick现在。我们不需要一个返回函数的函数,所以我们只需命名它onClick并删除额外的内部函数:
const onClick = (item: string) => {
const ix = items.indexOf(item)
const newItems = (ix > -1) ?
[...items.slice(0, ix), ...items.slice(ix + 1)]
:
[...items, item]
setItems(newItems)
}
Run Code Online (Sandbox Code Playgroud)
现在是最后一步。我们只需要记住这个函数,但是我们可以看到它需要最新的items. 换句话说, theitems是它的依赖项。所以即使我们useCallback现在添加,我们仍然需要将其添加items到依赖项中。我们知道,items选择某个项目时进行的更改useCallback不会有任何帮助。我们必须以某种方式消除items依赖关系。
幸运的是,setState实际上支持回调函数。例如:
const Animal = React.memo(({ selected, name, onClick }: AnimalProps) => (
<div onClick={() => onClick(name)}>
{`[${selected ? 'X' : ' '}]`} {name}
</div>
));
Run Code Online (Sandbox Code Playgroud)
这意味着,我们可以访问 current items,而无需items依赖。所以我们只需要更改setItems即可使用更新函数:
const onClick = (item: string) => {
const ix = items.indexOf(item)
const newItems = (ix > -1) ?
[...items.slice(0, ix), ...items.slice(ix + 1)]
:
[...items, item]
setItems(newItems)
}
Run Code Online (Sandbox Code Playgroud)
现在应该可以工作了。查看完整的工作代码:https://stackblitz.com/edit/react-ts-foxpnu ?file=index.tsx
您可以使用 React Developer Tools 检查渲染,或者只是将一些 conosle.log 添加到 Animal 组件。
| 归档时间: |
|
| 查看次数: |
735 次 |
| 最近记录: |