Skip to content

Bug - ToolbarFilter - Missing componentWillUnmount #12247

@YuliaKrimerman

Description

@YuliaKrimerman

Describe the problem

ToolbarFilter is a class component that registers its label count with the parent Toolbar via componentDidMount and componentDidUpdate by calling this.context.updateNumberFilters(categoryKey, labels.length). However, it has no componentWillUnmount lifecycle method to call updateNumberFilters(categoryKey, 0) when it unmounts.

This causes the Toolbar's internal filterInfo object (and staticFilterInfo) to retain stale counts for unmounted filters. Since the "Clear all filters" button visibility is driven by numberOfFilters > 0 (computed from filterInfo), the button persists even after all filter chips have been removed and their ToolbarFilter components have unmounted.

Affected components: ToolbarFilter, Toolbar, ToolbarLabelGroupContent

Relevant source:

  • ToolbarFilter.tsx — has componentDidMount and componentDidUpdate calling updateNumberFilters, but no componentWillUnmount
  • Toolbar.tsxupdateNumberFilters adds to filterInfo/staticFilterInfo but entries are never removed on unmount; showClearFiltersButton is computed as numberOfFilters > 0

How do you reproduce the problem?

  1. Render a Toolbar with clearAllFilters callback
  2. Inside, render ToolbarFilter components conditionally — mount them when filters are active (with populated labels), unmount them (return null) when filters are cleared
  3. Apply some filters so ToolbarFilter components mount with labels and the "Clear all filters" button appears
  4. Clear/reset all filters so the ToolbarFilter components unmount (return null)
  5. Observe that the filter chips disappear but the "Clear all filters" button remains visible
const MyToolbar = () => {
  const [filters, setFilters] = useState(['Active', 'Pending']);

  return (
    <Toolbar clearAllFilters={() => setFilters([])}>
      <ToolbarContent>
        {filters.length > 0 ? (
          <ToolbarFilter
            labels={filters}
            deleteLabel={(_, label) => setFilters(f => f.filter(v => v !== label))}
            deleteLabelGroup={() => setFilters([])}
            categoryName="Status"
          >
            {null}
          </ToolbarFilter>
        ) : null}
      </ToolbarContent>
    </Toolbar>
  );
};

Expected behavior

When all ToolbarFilter components are unmounted, the "Clear all filters" button should disappear because there are no active filters.

Is this issue blocking you?

Not blocking. Workaround: always keep ToolbarFilter mounted and pass labels={[]} instead of unmounting it. This ensures componentDidUpdate fires with labels.length === 0, which properly updates the internal count to 0.

Suggested fix — add componentWillUnmount to ToolbarFilter:

componentWillUnmount() {
  const { categoryName } = this.props;
  const categoryKey =
    typeof categoryName !== 'string' && categoryName.hasOwnProperty('key')
      ? categoryName.key
      : categoryName.toString();
  this.context.updateNumberFilters(categoryKey, 0);
}

Screenshots

N/A — the bug is a stale "Clear all filters" button remaining visible after all filter chips are gone.

What is your environment?

  • OS: macOS
  • Browser: Chrome
  • PatternFly version: @patternfly/react-core v6

What is your product and what release date are you targeting?

Kubeflow Model Registry — no hard release date for this fix since we have a workaround in place.

Any other information?

The documented PatternFly Toolbar examples avoid this bug by always keeping ToolbarFilter mounted and toggling the labels prop between a populated array and []. However, conditionally mounting/unmounting ToolbarFilter is a valid React pattern that should be supported. The missing componentWillUnmount cleanup is the root cause.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    Status

    Needs triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions