使用react-window构建无限滚动列表时遇到TypeScript编译错误TS2769的求助
解决react-window + TypeScript的无限滚动列表编译错误
这个错误的核心原因是你的Row组件的props类型没有匹配react-window的FixedSizeList要求的子组件props类型。具体来说,FixedSizeList会给它的子组件传递三个必填属性:index、style和data,但你的IRecipeProps只定义了前两个,缺少data,所以TypeScript抛出了类型不兼容的错误。
下面是具体的修复步骤:
步骤1:导入react-window的子组件props类型
首先从react-window中导入ListChildComponentProps类型,这个类型定义了列表子组件必须接收的props:
import { FixedSizeList as List, ListChildComponentProps } from "react-window";
步骤2:调整Row组件的props类型
你有两种方式来修正props类型:
方式一:让自定义props接口继承ListChildComponentProps
修改你的IRecipeProps接口,让它继承ListChildComponentProps(如果未来需要给Row添加额外props,这种方式更灵活):
interface IRecipeProps extends ListChildComponentProps<any> { // 这里可以添加你需要的额外props,当前场景不需要的话留空即可 } class Row extends PureComponent<IRecipeProps, IRecipeState> { // ... 原有render逻辑保持不变 }
方式二:直接使用ListChildComponentProps作为props类型
如果不需要自定义额外props,也可以直接把Row的props类型设置为ListChildComponentProps<any>:
class Row extends PureComponent<ListChildComponentProps<any>, IRecipeState> { render() { const { index, style } = this.props; // 不需要用到data属性也没关系,只要类型上兼容即可 let label; if (itemStatusMap[index] === LOADED) { label = `Row ${index}`; } else { label = "Loading..."; } return ( <div className="ListItem" style={style}> {label} </div> ); } }
为什么这样能解决问题?
react-window的FixedSizeList组件要求它的子组件必须是符合ComponentType<ListChildComponentProps<any>>类型的组件,这个类型强制要求组件接收data、index、style这三个props。通过让你的Row组件的props类型继承或直接使用这个类型,就满足了TypeScript的类型检查要求,编译错误也就消失了。
修改后的完整代码示例:
import { PureComponent } from "react"; import { FixedSizeList as List, ListChildComponentProps } from "react-window"; import InfiniteLoader from "react-window-infinite-loader"; import AutoSizer from "react-virtualized-auto-sizer"; const LOADING = 1; const LOADED = 2; let itemStatusMap: any = {}; const isItemLoaded = (index: number) => !!itemStatusMap[index]; const loadMoreItems = ( startIndex: number, stopIndex: number ): Promise<void> => { for (let index = startIndex; index <= stopIndex; index++) { itemStatusMap[index] = LOADING; } return new Promise((resolve) => setTimeout(() => { for (let index = startIndex; index <= stopIndex; index++) { itemStatusMap[index] = LOADED; } resolve(); console.log(Object.keys(itemStatusMap).length); }, 10) ); }; interface IRecipeProps extends ListChildComponentProps<any> {} interface IRecipeState {} class Row extends PureComponent<IRecipeProps, IRecipeState> { render() { const { index, style } = this.props; let label; if (itemStatusMap[index] === LOADED) { label = `Row ${index}`; } else { label = "Loading..."; } return ( <div className="ListItem" style={style}> {label} </div> ); } } export default function App() { return ( <AutoSizer> {({ height, width }) => ( <InfiniteLoader isItemLoaded={isItemLoaded} itemCount={50} loadMoreItems={loadMoreItems} > {({ onItemsRendered, ref }) => ( <List className="List" height={height} itemCount={50} itemSize={30} onItemsRendered={onItemsRendered} ref={ref} width={width} > {Row} </List> )} </InfiniteLoader> )} </AutoSizer> ); }
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

