'Using React class component returns Invalid hook call error

I am writing a simple class component that reorders some rows. For some reason I am getting the following error:

Invalid hook call. Hooks can only be called inside of the body of a function component.

And I am really puzzled because I am not using any hooks, just setState. Why does this error occur even though I am not using any hooks and how can I fix it? Here is my main.tsx code:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Grid, GridColumn as Column } from '@progress/kendo-react-grid';
import { DragAndDrop } from '@progress/kendo-react-common';
import { DragHint } from './drag-hint';
import { DraggableRow } from './dragable-row';
import { SelectionCell } from './selection-cell';

import products from './products.json';
import { Product } from './interfaces';

export let ReorderContext = React.createContext<{
  reorder: (dataItem: Product, direction: 'before' | 'after' | null) => void;
  dragStart: (dataItem: Product) => void;
  dragEnd: (dataItem?: Product) => void;
}>({ reorder: () => {}, dragStart: () => {}, dragEnd: () => {} });
export let SelectionContext = React.createContext<[Product[], any]>([
  [],
  () => {},
]);
export let IsSelectedContext = React.createContext<boolean>(false);
export const DragHintContext =
  React.createContext<React.RefObject<HTMLElement> | null>(null);
export let GridContext = React.createContext<React.RefObject<Grid> | null>(
  null
);

let grid = React.createRef(null);
let hint = React.createRef();
null;

class App extends React.Component {
  state = {
    gridData: products,
    selection: [],
    activeItem: null,
  };

  reorder(dataItem: any, direction: 'before' | 'after') {
    if (this.state.activeItem === dataItem) {
      return;
    }
    let reorderedData = this.state.gridData.slice();
    reorderedData = reorderedData.filter(
      (item) =>
        !this.state.selection.some(
          (selectedItem) => selectedItem.ProductID === item.ProductID
        )
    );
    let nextIndex = reorderedData.findIndex((p) => p === dataItem);

    reorderedData.splice(
      Math.max(nextIndex + (direction === 'before' ? 0 : 1), 0),
      0,
      ...this.state.selection
    );

    this.setState({ gridData: reorderedData });
  }

  dragStart(dataItem: any) {
    this.setState({ activeItem: dataItem });
  }

  dragEnd() {
    this.setState({ activeItem: null });
  }

  render() {
    return (
      <GridContext.Provider value={grid}>
        <DragHintContext.Provider value={hint}>
          <ReorderContext.Provider>
            <SelectionContext.Provider value={[this.state.selection]}>
              <DragAndDrop>
                <Grid
                  ref={grid}
                  style={{ height: '400px' }}
                  data={this.state.gridData}
                  dataItemKey={'ProductID'}
                  rowRender={(row, rowProps) => (
                    <DraggableRow elementProps={row.props} {...rowProps} />
                  )}
                >
                  <Column title="" width="80px" cell={SelectionCell} />
                  <Column field="ProductID" title="ID" width="60px" />
                  <Column field="ProductName" title="Name" width="200px" />
                  <Column field="Category.CategoryName" title="CategoryName" />
                  <Column field="UnitPrice" title="Price" width="80px" />
                  <Column field="UnitsInStock" title="In stock" width="80px" />
                </Grid>
                <DragHint
                  ref={hint}
                  portal={grid}
                  className="k-card"
                  style={{
                    display: this.state.activeItem ? undefined : 'none',
                  }}
                >
                  {this.state.activeItem && this.state.activeItem.ProductName}
                  {this.state.selection.length > 1 && (
                    <div
                      style={{
                        position: 'absolute',
                        pointerEvents: 'none',
                        bottom: 0,
                        right: 0,
                        background: 'red',
                        padding: 8,
                        width: 32,
                        color: 'white',
                        borderRadius: '50%',
                        transform: 'translate(50%, 50%)',
                      }}
                    >
                      {this.state.selection.length - 1}
                    </div>
                  )}
                </DragHint>
              </DragAndDrop>
            </SelectionContext.Provider>
          </ReorderContext.Provider>
        </DragHintContext.Provider>
      </GridContext.Provider>
    );
  }
}

ReactDOM.render(<App />, document.querySelector('my-app'));

And here is a reproducible example that contains the rest of the components as well:

https://stackblitz.com/edit/react-ms8hzy-wpfjcb?file=package.json



Solution 1:[1]

You are using React.useContext in your DraggableRow class component.(line 27 - 31)

My suggestion is to switch to functional components only. But if you want to use class based components I will refer you to this question which explains your problem.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 bguiz