可能你已经听过 React 中的一些设计模式,比如 Hoc(高阶组件),Render Props Pattern(渲染道具模式),Compound Pattern(组合组件),那我为什么要写这篇文章来重新描述它们呢。
因为与 Custom Hooks(自定义钩子)不同的是,我认为这些模式的好处可能并不那么显而易见。有时候你可能会想,即使你不使用某个模式,也能做到相同的效果。所以我会以开源社区的代码为例,来理解这些模式的必要性。
我们来看一看一些常见的虚拟列表实现的用户侧 Case。
<FlatList
renderItem={({item}) => <Item data={item} />}
/>
import {List} from 'react-virtualized';
const list = [
'Brian Vaughn',
];
function rowRenderer({
key,
index,
isScrolling,
isVisible,
style,
return (
<div key={key} style={style}>
{list[index]}
</div>
);
}
<List
width={300}
height={300}
rowCount={list.length}
rowHeight={20}
rowRenderer={rowRenderer}
/>
以及 react-window
, 也是差不多的代码,这里就不列举了。
我们很“偶然”地发现,这些虚拟列表库都不约而同地使用了 Render Props Pattern
,为什么呢?
试想一下,我们不用这个模式来实现 FlatList 可能是怎么样的。
<FlatList>
<FlatListHeader>...</FlatListHeader>
<FlatListItem>...</FlatListItem>
<FlatListFooter>...</FlatListFooter>
</FlatList>
一个非常”React”的方式是使用声明式的语法,直接传递 Children
。
很优雅,但是这种方式存在一个明显的性能问题,就是所有items都会被一次性创建,即使不在视图中。这违背了虚拟列表的初衷。
所以,当你需要懒加载的时候,直接传递 Children
是行不通的。因为这个时候列表渲染的控制权在”FlatList”这个容器,而不是使用者(我们)。
但其实也有一个折衷的方案,我们可以将 Children
作为函数来传入,这样也是可以实现懒加载的。
但这样做的缺点是,不如 Render Prop 来的灵活,比如 FlatList 很容易传递多个 Render Prop
<FlatList
renderItem={({item}) => <Item data={item} />}
renderScrollComponent={(props) => <View {...props} style={styles.list} />}
/>
总结: 当你需要懒加载的时候,想一想 Render Prop
模式。