diff --git a/frontend/src/metabase/dashboard/components/AddSeriesModal.jsx b/frontend/src/metabase/dashboard/components/AddSeriesModal.jsx
index 7146845333f042541e3111ecb7949d84a5631e82..9c2f1e7316a83b36b15e28fdbfea3da9d9f7436e 100644
--- a/frontend/src/metabase/dashboard/components/AddSeriesModal.jsx
+++ b/frontend/src/metabase/dashboard/components/AddSeriesModal.jsx
@@ -81,7 +81,7 @@ export default class AddSeriesModal extends Component {
             if (e.target.checked) {
                 if (getIn(dashcardData, [dashcard.id, card.id]) === undefined) {
                     this.setState({ state: "loading" });
-                    await this.props.fetchCardData(card, dashcard);
+                    await this.props.fetchCardData(card, dashcard, { reload: false, clear: true });
                 }
                 let sourceDataset = getIn(this.props.dashcardData, [dashcard.id, dashcard.card.id]);
                 let seriesDataset = getIn(this.props.dashcardData, [dashcard.id, card.id]);
diff --git a/frontend/src/metabase/dashboard/components/Dashboard.jsx b/frontend/src/metabase/dashboard/components/Dashboard.jsx
index 458170679f216ca31baa1d44bf37e83a5b8951fd..c14a429386bffc0106a8c58ea53498bac124c884 100644
--- a/frontend/src/metabase/dashboard/components/Dashboard.jsx
+++ b/frontend/src/metabase/dashboard/components/Dashboard.jsx
@@ -75,7 +75,7 @@ export default class Dashboard extends Component {
         if (this.props.params.dashboardId !== nextProps.params.dashboardId) {
             this.loadDashboard(nextProps.params.dashboardId);
         } else if (!_.isEqual(this.props.parameterValues, nextProps.parameterValues) || !this.props.dashboard) {
-            this.fetchDashboardCardData(nextProps, true);
+            this.fetchDashboardCardData(nextProps, { reload: false, clear: true });
         }
     }
 
@@ -275,12 +275,12 @@ export default class Dashboard extends Component {
     }
 
     // we don't call this initially because DashCards initiate their own fetchCardData
-    fetchDashboardCardData(props, clearExisting) {
+    fetchDashboardCardData(props, options) {
         if (props.dashboard) {
             for (const dashcard of props.dashboard.ordered_cards) {
                 const cards = [dashcard.card].concat(dashcard.series || []);
                 for (const card of cards) {
-                    props.fetchCardData(card, dashcard, clearExisting);
+                    props.fetchCardData(card, dashcard, options);
                 }
             }
         }
@@ -292,7 +292,7 @@ export default class Dashboard extends Component {
             refreshElapsed = 0;
 
             await this.props.fetchDashboard(this.props.params.dashboardId, this.props.location.query);
-            this.fetchDashboardCardData(this.props);
+            this.fetchDashboardCardData(this.props, { reload: true, clear: false });
         }
         this.setState({ refreshElapsed });
     }
@@ -304,6 +304,7 @@ export default class Dashboard extends Component {
 
         let parameters = dashboard && dashboard.parameters && dashboard.parameters.map(parameter =>
             <ParameterWidget
+                key={parameter.id}
                 className="ml1"
                 isEditing={isEditing}
                 isFullscreen={isFullscreen}
diff --git a/frontend/src/metabase/dashboard/components/parameters/widgets/TextWidget.jsx b/frontend/src/metabase/dashboard/components/parameters/widgets/TextWidget.jsx
index f57a6755b96d97fff6334b83aee71d9e5c568f6d..56e9828c255a7600ef0bbedce94723311ea0f6b7 100644
--- a/frontend/src/metabase/dashboard/components/parameters/widgets/TextWidget.jsx
+++ b/frontend/src/metabase/dashboard/components/parameters/widgets/TextWidget.jsx
@@ -12,7 +12,6 @@ export default class TextWidget extends Component {
     static propTypes = {
         value: PropTypes.any,
         setValue: PropTypes.func.isRequired,
-        onClose: PropTypes.func.isRequired,
         className: PropTypes.string,
         isEditing: PropTypes.bool,
         commitImmediately: PropTypes.bool,
diff --git a/frontend/src/metabase/dashboard/dashboard.js b/frontend/src/metabase/dashboard/dashboard.js
index 705b5031aeb9fe29560267517d49dafc883b323e..3801399842cb44b9c3cf60a2ac52debb9910ccf6 100644
--- a/frontend/src/metabase/dashboard/dashboard.js
+++ b/frontend/src/metabase/dashboard/dashboard.js
@@ -115,7 +115,7 @@ export const addCardToDashboard = function({ dashId, cardId }) {
             ...getPositionForNewDashCard(existingCards)
         };
         dispatch(createAction(ADD_CARD_TO_DASH)(dashcard));
-        dispatch(fetchCardData(card, dashcard));
+        dispatch(fetchCardData(card, dashcard, { reload: true, clear: true }));
     };
 }
 
@@ -134,12 +134,8 @@ export async function fetchDataOrError(dataPromise) {
     }
 }
 
-export const fetchCardData = createThunkAction(FETCH_CARD_DATA, function(card, dashcard, clearExisting = false) {
+export const fetchCardData = createThunkAction(FETCH_CARD_DATA, function(card, dashcard, { reload, clear } = {}) {
     return async function(dispatch, getState) {
-        if (clearExisting) {
-            dispatch(clearCardData(card.id, dashcard.id));
-        }
-
         // If the dataset_query was filtered then we don't have permisison to view this card, so
         // shortcircuit and return a fake 403
         if (!card.dataset_query) {
@@ -150,26 +146,49 @@ export const fetchCardData = createThunkAction(FETCH_CARD_DATA, function(card, d
             };
         }
 
-        let result = null;
+        const { dashboardId, dashboards, parameterValues, dashcardData } = getState().dashboard;
+        const dashboard = dashboards[dashboardId];
 
         // if we have a parameter, apply it to the card query before we execute
-        let { dashboardId } = getState().dashboard;
-        let { dashboards, parameterValues } = getState().dashboard;
+        const datasetQuery = applyParameters(card, dashboard.parameters, parameterValues, dashcard && dashcard.parameter_mappings);
 
-        let dashboard = dashboards[dashboardId];
+        if (!reload) {
+            // if reload not set, check to see if the last result has the same query dict and return that
+            const lastResult = i.getIn(dashcardData, [dashcard.id, card.id]);
+            // "constraints" is added by the backend, remove it when comparing
+            if (lastResult && angular.equals(_.omit(lastResult.json_query, "constraints"), datasetQuery)) {
+                return {
+                    dashcard_id: dashcard.id,
+                    card_id: card.id,
+                    result: lastResult
+                };
+            }
+        }
 
-        const datasetQuery = applyParameters(card, dashboard.parameters, parameterValues, dashcard && dashcard.parameter_mappings);
+        if (clear) {
+            // clears the card data to indicate the card is reloading
+            dispatch(clearCardData(card.id, dashcard.id));
+        }
+
+        let result = null;
 
+        // start a timer that will fetch the expected card duration if the query takes too long
         let slowCardTimer = setTimeout(() => {
             if (result === null) {
                 dispatch(fetchCardDuration(card, datasetQuery));
             }
         }, DATASET_SLOW_TIMEOUT);
 
+        // make the actual request
         result = await fetchDataOrError(CardApi.query({cardID: card.id, parameters: datasetQuery.parameters}));
 
         clearTimeout(slowCardTimer);
-        return { dashcard_id: dashcard.id, card_id: card.id, result };
+
+        return {
+            dashcard_id: dashcard.id,
+            card_id: card.id,
+            result: result
+        };
     };
 });