Mailspring/app/internal_packages/thread-sharing
Ben Gotow 6e19fca2f3
Replace componentWillReceiveProps usage (#2546)
* Migrate componentWillReceiveProps to componentDidUpdate for React 17 compatibility

This commit migrates all 47 usages of componentWillReceiveProps across the
codebase to use the componentDidUpdate pattern, which is the recommended
approach for React 17+.

Migration pattern:
- Changed from comparing `nextProps` vs `this.props` to `prevProps` vs `this.props`
- Merged logic into existing componentDidUpdate methods where applicable
- Also migrated componentWillMount to componentDidMount + state initialization
  where needed (message-list-scroll-tooltip.tsx, thread-list-scroll-tooltip.tsx)

Files migrated:
- Core components: spinner, mail-important-icon, empty-list-state, fixed-popover,
  time-picker, mini-month-view, dropdown-menu, injected-component,
  injected-component-set, sheet, sheet-toolbar, scenario-editor-row,
  swipe-container, outline-view-item, menu, multiselect-list,
  key-commands-region, resizable-region, lazy-rendered-list,
  tokenizing-text-field, webview
- Decorators/HOCs: listens-to-flux-store, listens-to-observable,
  flux-container, inflates-draft-client-id
- Internal packages: composer-header, send-action-button, toolbar-category-picker,
  autoload-images-header, message-item-body, message-item-container,
  newsletter-signup, preferences-account-details, preferences-appearance,
  send-later-status, send-reminders-composer-button, thread-search-bar,
  calendar-event-popover, activity dashboard root, label-picker-popover,
  move-picker-popover, copy-button, thread-sharing-button, undo-redo-toast,
  scroll tooltips for message-list and thread-list

* Fix race conditions and edge cases in componentWillReceiveProps migration

This commit addresses critical issues found during review of the
lifecycle migration from componentWillReceiveProps to componentDidUpdate.

## inflates-draft-client-id.tsx
- Clear draft/session state immediately when headerMessageId changes
- Prevents rendering with mismatched draft content during async fetch

## listens-to-observable.tsx
- Added subscription generation counter (subscriptionId)
- Guards against stale observable emissions updating state
- Prevents race condition where old observable emits after props change

## flux-container.tsx
- Store getStateFromStores as instance property (_getStateFromStores)
- Reorder: setup listeners BEFORE setState in componentDidUpdate
- Listeners now reference instance property, not captured props
- Prevents stale closures and race conditions with store events

## multiselect-list.tsx
- Replace expensive _.isEqual(prevProps, this.props) with specific checks
- Only check dataSource and columns identity (props that affect state)
- Remove unnecessary teardown/setup (WorkspaceStore listener is static)
- Significant performance improvement + eliminates race condition

## menu.tsx
- Added getDerivedStateFromProps to clamp selectedIndex before render
- Prevents undefined access when items array shrinks
- Simplified componentDidUpdate to only handle selection preservation
- Added bounds checking before accessing prevProps.items[oldSelectedIndex]

* Use getDerivedStateFromProps to eliminate state/props desync renders

This commit addresses the remaining MEDIUM-risk issues where components
could render with state out of sync with props for one frame.

## inflates-draft-client-id.tsx
- Added getDerivedStateFromProps to detect headerMessageId changes
- Clears draft/session state BEFORE render (not after in componentDidUpdate)
- Track _lastHeaderMessageId in state to detect prop changes
- Eliminates the one-frame mismatch between draft and headerMessageId

## multiselect-list.tsx
- Added getDerivedStateFromProps to update handler/columns before render
- Store _checkmarkColumn in state (created once in constructor)
- Track _prevDataSource and _prevColumns to detect changes
- Handler is now always in sync with current dataSource
- Removed _getStateFromStores (logic moved to getDerivedStateFromProps)
- _onChange now triggers empty setState to let getDerivedStateFromProps handle updates

## menu.tsx
- Enhanced getDerivedStateFromProps to handle selection preservation
- Added selectedItemKey and prevItems to state
- When items change, finds selected item by key in new array
- Selection is preserved BEFORE render (not after in componentDidUpdate)
- Removed selection preservation logic from componentDidUpdate
- Updated all selection state changes to include selectedItemKey

All three components now have no render where state is out of sync with props.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-06 15:27:45 -06:00
..
assets Thread sharing polish and active state 2018-04-03 09:50:42 -07:00
lib Replace componentWillReceiveProps usage (#2546) 2026-01-06 15:27:45 -06:00
styles Spring cleaning: Drop the -webkit css prefix where possible 2019-06-11 00:53:19 -05:00
package.json Add a isIdentityRequired flag to plugin package.json’s, disable them when identity is not present 2021-03-29 17:57:19 -05:00