Skip to content

refactor: convert all class components to function components (#431)#444

Open
nahrinoda wants to merge 9 commits into
Autodesk:masterfrom
nahrinoda:fix/class-to-function-components
Open

refactor: convert all class components to function components (#431)#444
nahrinoda wants to merge 9 commits into
Autodesk:masterfrom
nahrinoda:fix/class-to-function-components

Conversation

@nahrinoda

@nahrinoda nahrinoda commented Jun 19, 2026

Copy link
Copy Markdown

Summary

Converts all 7 class components to function components with React hooks, consolidates all TypeScript type definitions into a single types.ts file, removes PropTypes in favor of TypeScript, and improves type safety — fully resolving #431.

Components converted

Component Complexity Key patterns used
ExpandIcon Simple React.memo, useCallback, JS default params
ColumnResizer Complex useRef for drag state, ref-based prop access for DOM listeners to avoid stale closures, useEffect cleanup
TableRow Complex useState, useRef, useMemo, split componentDidUpdate into multiple useEffect hooks for re-measurement cycle
TableHeader Medium forwardRef + useImperativeHandle (scrollTo, forceUpdate), useReducer for force-update trick
GridTable Medium forwardRef + useImperativeHandle (7 imperative methods), useMemo with memoize-one
Column Trivial Function returning null with static property assignments
BaseTable Complex useState (5 state vars), 17+ useRef instances, useMemo for memoized helpers, throttle/debounce with stable identity via useMemo, forwardRef + useImperativeHandle (14 public API methods), useEffect for lifecycle

Migration patterns

  • React.PureComponent -> React.memo wrapper
  • static defaultProps -> JS default parameters in destructuring
  • Constructor .bind(this) -> useCallback hooks
  • Instance variables -> useRef
  • this.state / setState -> useState
  • componentDidMount / componentDidUpdate -> useEffect
  • componentWillUnmount -> useEffect cleanup return
  • forceUpdate() -> useReducer counter trick
  • Imperative class methods -> forwardRef + useImperativeHandle
  • memoize-one calls -> useMemo(() => memoize(...), [])
  • Throttled/debounced handlers -> useMemo with stable identity + useRef for latest props

Type consolidation

Moved all 14 component prop interfaces and handle types from individual component files into the central src/types.ts, joining the 15 types already there. All types are re-exported from index.ts for consumer access.

Types moved Source file
AutoResizerProps AutoResizer.tsx
BaseTableProps, BaseTableHandle BaseTable.tsx
ColumnResizerProps ColumnResizer.tsx
ExpandIconProps ExpandIcon.tsx
GridTableProps, GridTableHandle GridTable.tsx
SortIndicatorProps SortIndicator.tsx
TableCellProps TableCell.tsx
TableHeaderProps, TableHeaderHandle TableHeader.tsx
TableHeaderCellProps TableHeaderCell.tsx
TableHeaderRowProps TableHeaderRow.tsx
TableRowProps TableRow.tsx

Best practices applied

PropTypes removal — Removed PropTypes from all 12 component files (~520 lines deleted). TypeScript interfaces in types.ts fully supersede runtime prop validation. The ESLint react/prop-types rule is now disabled. PropTypes are removed from the React 19 package entirely.

Type safety improvements — Replaced loose [key: string]: any index signatures with React.HTMLAttributes<HTMLDivElement> in ColumnResizerProps, ExpandIconProps, and SortIndicatorProps. Remaining index signatures are documented with comments explaining they exist for legitimate pass-through patterns (e.g., GridTable forwarding props to react-window Grid, or row/header components spreading props to configurable tag elements).

Future migration paths

forwardRef deprecation (React 19)forwardRef is deprecated in React 19; ref becomes a regular prop. Components using forwardRef (BaseTable, GridTable, TableHeader) can be simplified when upgrading. No action needed now.

React Compiler readiness — The React Compiler (v1.0, Oct 2025) auto-memoizes components, eliminating the need for React.memo, useCallback, and useMemo. The current code is compatible — these wrappers can be safely removed when adopting the compiler.

What is NOT changed

  • No behavioral changes — the public API and rendering output are identical
  • All import type usage is correct throughout (verified via audit)

Test plan

  • All 22 existing snapshot tests pass unchanged
  • ESLint passes with zero errors
  • TypeScript build (tsc --emitDeclarationOnly) passes with zero errors
  • Full build (Babel CJS/ESM + TypeScript declarations + Sass) completes successfully
  • Manual verification of demo site examples (expand/collapse, column resize, sorting, frozen columns, dynamic row height, scrolling)
Screen.Recording.2026-06-19.at.4.39.19.PM.mov

Resolves #431

nahrinoda and others added 2 commits June 19, 2026 11:29
…sk#431)

Convert ExpandIcon from React.PureComponent class to a function component
wrapped with React.memo to preserve shallow prop comparison behavior.

- Replace static defaultProps with JS default parameters
- Replace constructor bind with useCallback hook
- Move propTypes to a static property on the function component
- No behavioral changes

Resolves part of Autodesk#431

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…odesk#431)

Convert ColumnResizer from React.PureComponent class to a function component
wrapped with React.memo.

- Replace instance variables with useRef for mutable drag state
  (isDragging, lastX, width, handleRef)
- Use refs for props accessed in DOM event listeners (column, onResize,
  onResizeStop, minWidth) to avoid stale closures
- Replace constructor binds with useCallback hooks
- Replace componentWillUnmount with useEffect cleanup
- Replace static defaultProps with JS default parameters
- Move propTypes to a property on the function component
- No behavioral changes

Resolves part of Autodesk#431

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nahrinoda nahrinoda marked this pull request as draft June 19, 2026 15:31
nahrinoda and others added 5 commits June 19, 2026 11:33
…#431)

Convert TableRow from React.PureComponent class to a function component
wrapped with React.memo.

- Replace this.state { measured } with useState hook
- Replace this.ref with useRef hook
- Replace componentDidMount with useEffect for initial measurement
- Replace componentDidUpdate re-measure cycle with two useEffect hooks:
  one to detect when re-measurement is needed, one to trigger it
- Replace _getEventHandlers with useMemo
- Replace _handleExpand with useCallback
- Replace static defaultProps with JS default parameters
- Move propTypes to a property on the function component
- No behavioral changes

Resolves part of Autodesk#431

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…esk#431)

Convert TableHeader from React.PureComponent class to a function component
using forwardRef + useImperativeHandle + React.memo.

- Use forwardRef to expose imperative scrollTo() and forceUpdate() methods
- Replace this.headerRef with useRef hook
- Replace constructor-bound render methods with useCallback hooks
- Use useReducer to implement forceUpdate behavior
- Update GridTable header ref type to TableHeaderHandle
- No behavioral changes

Resolves part of Autodesk#431

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…k#431)

- Replace class with forwardRef + React.memo function component
- Convert instance methods to useImperativeHandle (resetAfterRowIndex,
  forceUpdateTable, scrollToPosition, scrollToTop, scrollToLeft,
  scrollToRow, getTotalRowsHeight)
- Convert memoized helpers to useMemo/useCallback hooks
- Update BaseTable ref types to use GridTableHandle interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace class with function component returning null
- Move static properties (Alignment, FrozenDirection) to direct assignments

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…k#431)

- Replace class with forwardRef + React.memo function component
- Convert state to useState hooks (scrollbarSize, hoveredRowKey,
  resizingKey, resizingWidth, expandedRowKeys)
- Convert instance variables to useRef (scroll position, row height
  maps, scrollbar sizes, data tracking, column manager)
- Convert memoized helpers to useMemo with memoize-one
- Convert throttled column resize handler to stable useMemo callback
- Convert debounced row height update to stable useMemo callback
- Convert lifecycle methods to useEffect hooks
- Expose public API via useImperativeHandle (getDOMNode,
  getColumnManager, scroll methods, expand methods, etc.)
- Add BaseTableHandle interface for imperative ref type
- Maintain static properties (Column, PlaceholderKey) on memo wrapper

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nahrinoda nahrinoda changed the title refactor: convert ExpandIcon from class to function component refactor: convert all class components to function components (#431) Jun 19, 2026
nahrinoda and others added 2 commits June 19, 2026 12:54
- Move all component prop interfaces and handle types from individual
  component files into the central src/types.ts
- Components now import their types from types.ts instead of defining
  them locally
- Re-export all type definitions from index.ts for consumer access
- No behavioral changes — only import paths changed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove PropTypes from all 12 component files (TypeScript interfaces supersede them)
- Disable ESLint react/prop-types rule
- Replace [key: string]: any with React.HTMLAttributes<HTMLDivElement> in
  ColumnResizerProps, ExpandIconProps, and SortIndicatorProps
- Add explanatory comments for remaining index signatures in pass-through components
- Remove prop-types import from all source files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nahrinoda nahrinoda marked this pull request as ready for review June 19, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multiple console warnings due to Class Component structure

1 participant