fix(widget-builder): Avoid unnecessary queries for events
diff --git a/static/app/views/dashboardsV2/widgetBuilder/buildSteps/visualizationStep.spec.tsx b/static/app/views/dashboardsV2/widgetBuilder/buildSteps/visualizationStep.spec.tsx
index 0f61acf85cccc..c597cbef722c3 100644
--- a/static/app/views/dashboardsV2/widgetBuilder/buildSteps/visualizationStep.spec.tsx
+++ b/static/app/views/dashboardsV2/widgetBuilder/buildSteps/visualizationStep.spec.tsx
@@ -126,7 +126,7 @@ describe('VisualizationStep', function () {
       jest.advanceTimersByTime(DEFAULT_DEBOUNCE_DURATION + 1);
     });
 
-    await waitFor(() => expect(eventsMock).toHaveBeenCalledTimes(2));
+    await waitFor(() => expect(eventsMock).toHaveBeenCalledTimes(1));
   });
 
   it('displays stored data alert', async function () {
@@ -220,4 +220,46 @@ describe('VisualizationStep', function () {
       )
     );
   });
+
+  it('does not trigger an extra events request when adding a column', async function () {
+    const {eventsMock} = mockRequests(organization.slug);
+    render(
+      <WidgetBuilder
+        route={{}}
+        router={router}
+        routes={router.routes}
+        routeParams={router.params}
+        location={{
+          ...router.location,
+          query: {
+            ...router.location.query,
+            release: ['v1'],
+          },
+        }}
+        dashboard={{
+          id: 'new',
+          title: 'Dashboard',
+          createdBy: undefined,
+          dateCreated: '2020-01-01T00:00:00.000Z',
+          widgets: [],
+          projects: [],
+          filters: {},
+        }}
+        onSave={jest.fn()}
+        params={{
+          orgId: organization.slug,
+          dashboardId: 'new',
+        }}
+      />,
+      {
+        context: routerContext,
+        organization,
+      }
+    );
+
+    userEvent.click(screen.getByText('Add a Column'));
+
+    // Only called once on the initial render
+    await waitFor(() => expect(eventsMock).toHaveBeenCalledTimes(1));
+  });
 });
diff --git a/static/app/views/dashboardsV2/widgetCard/genericWidgetQueries.tsx b/static/app/views/dashboardsV2/widgetCard/genericWidgetQueries.tsx
index 031658ba211ae..1468a48dd6464 100644
--- a/static/app/views/dashboardsV2/widgetCard/genericWidgetQueries.tsx
+++ b/static/app/views/dashboardsV2/widgetCard/genericWidgetQueries.tsx
@@ -1,6 +1,7 @@
 import {Component} from 'react';
 import cloneDeep from 'lodash/cloneDeep';
 import isEqual from 'lodash/isEqual';
+import omit from 'lodash/omit';
 
 import {Client, ResponseMeta} from 'sentry/api';
 import {isSelectionEqual} from 'sentry/components/organizations/pageFilters/utils';
@@ -134,6 +135,10 @@ class GenericWidgetQueries<SeriesResponse, TableResponse> extends Component<
       .reduce(
         ([names, queries]: [string[], Omit<WidgetQuery, 'name'>[]], {name, ...rest}) => {
           names.push(name);
+          rest.fields = rest.fields?.filter(field => !!field) ?? [];
+
+          // Ignore aliases because changing alias does not need a query
+          rest = omit(rest, 'fieldAliases');
           queries.push(rest);
           return [names, queries];
         },
@@ -151,6 +156,10 @@ class GenericWidgetQueries<SeriesResponse, TableResponse> extends Component<
       .reduce(
         ([names, queries]: [string[], Omit<WidgetQuery, 'name'>[]], {name, ...rest}) => {
           names.push(name);
+          rest.fields = rest.fields?.filter(field => !!field) ?? [];
+
+          // Ignore aliases because changing alias does not need a query
+          rest = omit(rest, 'fieldAliases');
           queries.push(rest);
           return [names, queries];
         },
@@ -163,7 +172,7 @@ class GenericWidgetQueries<SeriesResponse, TableResponse> extends Component<
         : widget.limit !== prevProps.widget.limit ||
           !isEqual(widget.displayType, prevProps.widget.displayType) ||
           !isEqual(widget.interval, prevProps.widget.interval) ||
-          !isEqual(widgetQueries, prevWidgetQueries) ||
+          !isEqual(new Set(widgetQueries), new Set(prevWidgetQueries)) ||
           !isEqual(this.props.dashboardFilters, prevProps.dashboardFilters) ||
           !isSelectionEqual(selection, prevProps.selection) ||
           cursor !== prevProps.cursor

The events dataset would re-query in some unnecessary cases. 

Reordering the fields, adding an alias, and adding a new field shouldn't immediately trigger a request when it's blank

There are some unnecessary queries in the release data set but I'm going to handle those separately.