reactjstypescriptreact-virtualizedreact-window

Type 'typeof Row' is not assignable to type 'ComponentType<ListChildComponentProps<any>> & ReactNode'. TS2769


I am putting together an infinite scrolling list using react-window and am getting a typescript build error. I've searched stack overflow and fixed a few other previous errors but I was unable to fix this last one.

Here is the code in codesandbox: https://codesandbox.io/s/pedantic-leakey-bw5fv?file=/src/App.tsx

Same copy of the code as in the link here:

import { PureComponent } from "react";
import { FixedSizeList as List } 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 {
  index: number;
  style: 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>
  );
}

And the error:

Failed to compile

/home/user/code/frontend/src/components/Table/table2.tsx
TypeScript error in /home/user/code/frontend/src/components/Table/table2.tsx(72,15):
No overload matches this call.
  Overload 1 of 2, '(props: FixedSizeListProps<any> | Readonly<FixedSizeListProps<any>>): FixedSizeList<any>', gave the following error.
    Type 'typeof Row' is not assignable to type 'ComponentType<ListChildComponentProps<any>> & ReactNode'.
      Type 'typeof Row' is not assignable to type 'ComponentClass<ListChildComponentProps<any>, any>'.
        Construct signature return types 'Row' and 'Component<ListChildComponentProps<any>, any, any>' are incompatible.
          The types of 'props' are incompatible between these types.
            Type 'Readonly<IRecipeProps> & Readonly<{ children?: ReactNode; }>' is not assignable to type 'Readonly<ListChildComponentProps<any>> & Readonly<{ children?: ReactNode; }>'.
              Property 'data' is missing in type 'Readonly<IRecipeProps> & Readonly<{ children?: ReactNode; }>' but required in type 'Readonly<ListChildComponentProps<any>>'.
  Overload 2 of 2, '(props: FixedSizeListProps<any>, context: any): FixedSizeList<any>', gave the following error.
    Type 'typeof Row' is not assignable to type 'ComponentType<ListChildComponentProps<any>> & ReactNode'.  TS2769

    70 |               width={width}
    71 |             >
  > 72 |               {Row}
       |               ^
    73 |             </List>
    74 |           )}
    75 |         </InfiniteLoader>

Solution

  • You just need to add data property to Row component props.

    import React, { PureComponent } from "react";
    import { FixedSizeList as List } 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 {
        index: number;
        style: any;
        data: Array<unknown> // you need to add `data` property
    }
    
    class Row extends PureComponent<IRecipeProps> {
        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>
        );
    }
    

    Playground