diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js index 4043c1cdae..273169f120 100644 --- a/ui/.eslintrc.js +++ b/ui/.eslintrc.js @@ -15,6 +15,7 @@ module.exports = { }, extends: [ 'plugin:react/recommended', + 'plugin:react-hooks/recommended', 'prettier/react', 'prettier/@typescript-eslint', 'eslint:recommended', diff --git a/ui/cypress/e2e/login.test.ts b/ui/cypress/e2e/login.test.ts index 230b32df0c..124e82db05 100644 --- a/ui/cypress/e2e/login.test.ts +++ b/ui/cypress/e2e/login.test.ts @@ -12,17 +12,31 @@ describe('The Login Page', () => { user = u }) - cy.setupUser() + cy.setupUser().then(({body}) => { + cy.wrap(body.org.id).as('orgID') + }) cy.visit('/') }) - it('can login', () => { + it('can login and logout', () => { cy.getByInputName('username').type(user.username) cy.getByInputName('password').type(user.password) cy.get('button[type=submit]').click() cy.getByTestID('tree-nav').should('exist') + + cy.getByTestID('logout--button').click() + + cy.getByTestID('signin-page--content').should('exist') + + // try to access a protected route + cy.get('@orgID').then(orgID => { + cy.visit(`/orgs/${orgID}`) + }) + + // assert that user is routed to signin + cy.getByTestID('signin-page--content').should('exist') }) describe('login failure', () => { diff --git a/ui/package.json b/ui/package.json index 89f871a82c..1d89b72e1f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -97,6 +97,7 @@ "eslint-config-prettier": "^6.5.0", "eslint-plugin-jest": "^23.0.2", "eslint-plugin-react": "^7.16.0", + "eslint-plugin-react-hooks": "^4.0.5", "express": "^4.14.0", "file-loader": "^4.1.0", "fork-ts-checker-webpack-plugin": "^1.4.3", diff --git a/ui/src/Logout.tsx b/ui/src/Logout.tsx index 7c6ac33689..7564fca7f4 100644 --- a/ui/src/Logout.tsx +++ b/ui/src/Logout.tsx @@ -1,7 +1,7 @@ // Libraries import {FC, useEffect} from 'react' -import {connect, ConnectedProps} from 'react-redux' -import {withRouter, RouteComponentProps} from 'react-router-dom' +import {useDispatch} from 'react-redux' +import {RouteComponentProps} from 'react-router-dom' // APIs import {postSignout} from 'src/client' @@ -12,36 +12,31 @@ import {CLOUD, CLOUD_URL, CLOUD_LOGOUT_PATH} from 'src/shared/constants' // Components import {reset} from 'src/shared/actions/flags' -type ReduxProps = ConnectedProps -type Props = ReduxProps & RouteComponentProps +type Props = RouteComponentProps -const Logout: FC = ({history, resetFeatureFlags}) => { - const handleSignOut = async () => { - if (CLOUD) { - window.location.href = `${CLOUD_URL}${CLOUD_LOGOUT_PATH}` - return - } else { - const resp = await postSignout({}) - - if (resp.status !== 204) { - throw new Error(resp.data.message) - } - - history.push(`/signin`) - } - } +const Logout: FC = ({history}) => { + const dispatch = useDispatch() useEffect(() => { - resetFeatureFlags() + const handleSignOut = async () => { + if (CLOUD) { + window.location.href = `${CLOUD_URL}${CLOUD_LOGOUT_PATH}` + return + } else { + const resp = await postSignout({}) + + if (resp.status !== 204) { + throw new Error(resp.data.message) + } + + history.push(`/signin`) + } + } + dispatch(reset()) handleSignOut() - }, []) + }, [dispatch, history]) + return null } -const mdtp = { - resetFeatureFlags: reset, -} - -const connector = connect(null, mdtp) - -export default connector(withRouter(Logout)) +export default Logout diff --git a/ui/src/alerting/components/AlertHistoryQueryParams.tsx b/ui/src/alerting/components/AlertHistoryQueryParams.tsx index 697e433699..ebd4f21d7f 100644 --- a/ui/src/alerting/components/AlertHistoryQueryParams.tsx +++ b/ui/src/alerting/components/AlertHistoryQueryParams.tsx @@ -32,7 +32,7 @@ const AlertHistoryQueryParams: FC = ({ }, history ) - }, [searchInput, historyType]) + }, [searchInput, historyType, history]) return null } diff --git a/ui/src/alerting/components/builder/ThresholdCondition.tsx b/ui/src/alerting/components/builder/ThresholdCondition.tsx index 7ecd024bb3..4ad8ee87fa 100644 --- a/ui/src/alerting/components/builder/ThresholdCondition.tsx +++ b/ui/src/alerting/components/builder/ThresholdCondition.tsx @@ -56,12 +56,12 @@ const ThresholdCondition: FC = ({ get(threshold, 'max', 100), ]) + const min = get(threshold, 'value') || get(threshold, 'min', inputs[0]) + const max = get(threshold, 'max', inputs[1]) + useEffect(() => { - changeInputs([ - get(threshold, 'value') || get(threshold, 'min', inputs[0]), - get(threshold, 'max', inputs[1]), - ]) - }, [threshold]) + changeInputs([min, max]) + }, [min, max]) const [yDomain] = useCheckYDomain(table.getColumn('_value', 'number'), []) diff --git a/ui/src/buckets/components/CreateBucketButton.tsx b/ui/src/buckets/components/CreateBucketButton.tsx index be24f150d5..1b29b0491c 100644 --- a/ui/src/buckets/components/CreateBucketButton.tsx +++ b/ui/src/buckets/components/CreateBucketButton.tsx @@ -1,6 +1,6 @@ // Libraries import React, {FC, useEffect} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' // Components import { @@ -11,10 +11,7 @@ import { } from '@influxdata/clockface' // Actions -import { - checkBucketLimits as checkBucketLimitsAction, - LimitStatus, -} from 'src/cloud/actions/limits' +import {checkBucketLimits, LimitStatus} from 'src/cloud/actions/limits' import {showOverlay, dismissOverlay} from 'src/overlays/actions/overlays' // Utils @@ -28,14 +25,14 @@ type Props = ReduxProps const CreateBucketButton: FC = ({ limitStatus, - checkBucketLimits, onShowOverlay, onDismissOverlay, }) => { + const dispatch = useDispatch() useEffect(() => { // Check bucket limits when component mounts - checkBucketLimits() - }, []) + dispatch(checkBucketLimits()) + }, [dispatch]) const limitExceeded = limitStatus === LimitStatus.EXCEEDED const text = 'Create Bucket' @@ -77,7 +74,6 @@ const mstp = (state: AppState) => { const mdtp = { onShowOverlay: showOverlay, onDismissOverlay: dismissOverlay, - checkBucketLimits: checkBucketLimitsAction, } const connector = connect(mstp, mdtp) diff --git a/ui/src/buckets/components/DemoDataDropdown.tsx b/ui/src/buckets/components/DemoDataDropdown.tsx index 1226037b38..15c826971c 100644 --- a/ui/src/buckets/components/DemoDataDropdown.tsx +++ b/ui/src/buckets/components/DemoDataDropdown.tsx @@ -1,12 +1,12 @@ // Libraries import React, {FC, useEffect} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' import {get, sortBy} from 'lodash' // Actions import { getDemoDataBucketMembership as getDemoDataBucketMembershipAction, - getDemoDataBuckets as getDemoDataBucketsAction, + getDemoDataBuckets, } from 'src/cloud/actions/demodata' // Components @@ -22,11 +22,11 @@ const DemoDataDropdown: FC = ({ ownBucketsByID, demoDataBuckets, getDemoDataBucketMembership, - getDemoDataBuckets, }) => { + const dispatch = useDispatch() useEffect(() => { - getDemoDataBuckets() - }, []) + dispatch(getDemoDataBuckets()) + }, [dispatch]) if (!demoDataBuckets.length) { return null @@ -109,7 +109,6 @@ const mstp = (state: AppState) => ({ const mdtp = { getDemoDataBucketMembership: getDemoDataBucketMembershipAction, - getDemoDataBuckets: getDemoDataBucketsAction, } const connector = connect(mstp, mdtp) diff --git a/ui/src/buckets/components/UpdateBucketOverlay.tsx b/ui/src/buckets/components/UpdateBucketOverlay.tsx index 80d6437bc4..f8ce92a2d4 100644 --- a/ui/src/buckets/components/UpdateBucketOverlay.tsx +++ b/ui/src/buckets/components/UpdateBucketOverlay.tsx @@ -5,9 +5,10 @@ import React, { useState, ChangeEvent, FormEvent, + useCallback, } from 'react' import {withRouter, RouteComponentProps} from 'react-router-dom' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' import {get} from 'lodash' // Components @@ -35,7 +36,6 @@ import {OwnBucket} from 'src/types' interface DispatchProps { onUpdateBucket: typeof updateBucket - onNotify: typeof notify } type ReduxProps = ConnectedProps @@ -43,23 +43,27 @@ type Props = ReduxProps & RouteComponentProps<{bucketID: string; orgID: string}> const UpdateBucketOverlay: FunctionComponent = ({ onUpdateBucket, - onNotify, match, history, }) => { const {orgID, bucketID} = match.params + const dispatch = useDispatch() const [bucketDraft, setBucketDraft] = useState(null) const [loadingStatus, setLoadingStatus] = useState(RemoteDataState.Loading) const [retentionSelection, setRetentionSelection] = useState(DEFAULT_SECONDS) + const handleClose = useCallback(() => { + history.push(`/orgs/${orgID}/load-data/buckets`) + }, [orgID, history]) + useEffect(() => { const fetchBucket = async () => { const resp = await api.getBucket({bucketID}) if (resp.status !== 200) { - onNotify(getBucketFailed(bucketID, resp.data.message)) + dispatch(notify(getBucketFailed(bucketID, resp.data.message))) handleClose() return } @@ -74,7 +78,7 @@ const UpdateBucketOverlay: FunctionComponent = ({ setLoadingStatus(RemoteDataState.Done) } fetchBucket() - }, [bucketID]) + }, [bucketID, handleClose, dispatch]) const handleChangeRetentionRule = (everySeconds: number): void => { setBucketDraft({ @@ -112,10 +116,6 @@ const UpdateBucketOverlay: FunctionComponent = ({ setBucketDraft({...bucketDraft, [key]: value}) } - const handleClose = () => { - history.push(`/orgs/${orgID}/load-data/buckets`) - } - const handleClickRename = () => { history.push(`/orgs/${orgID}/load-data/buckets/${bucketID}/rename`) } @@ -157,7 +157,6 @@ const UpdateBucketOverlay: FunctionComponent = ({ const mdtp = { onUpdateBucket: updateBucket, - onNotify: notify, } const connector = connect(null, mdtp) diff --git a/ui/src/checks/components/CheckMatchingRulesCard.tsx b/ui/src/checks/components/CheckMatchingRulesCard.tsx index 902920a5ef..ceaeed6c85 100644 --- a/ui/src/checks/components/CheckMatchingRulesCard.tsx +++ b/ui/src/checks/components/CheckMatchingRulesCard.tsx @@ -94,7 +94,7 @@ const CheckMatchingRulesCard: FC = ({orgID, tags, queryResults}) => { }) getMatchingRules() - }, [tags, queryResults]) + }, [tags, queryResults]) // eslint-disable-line react-hooks/exhaustive-deps let contents: JSX.Element diff --git a/ui/src/checks/components/EditCheckEO.tsx b/ui/src/checks/components/EditCheckEO.tsx index 2403ef322d..8b8ce2385b 100644 --- a/ui/src/checks/components/EditCheckEO.tsx +++ b/ui/src/checks/components/EditCheckEO.tsx @@ -1,7 +1,7 @@ // Libraries import React, {FunctionComponent, useEffect} from 'react' import {withRouter, RouteComponentProps} from 'react-router-dom' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' import {get} from 'lodash' // Components @@ -30,8 +30,6 @@ const EditCheckEditorOverlay: FunctionComponent = ({ onUpdateAlertBuilderName, onResetAlertBuilder, onSaveCheckFromTimeMachine, - onExecuteQueries, - onGetCheckForTimeMachine, activeTimeMachineID, status, history, @@ -42,13 +40,16 @@ const EditCheckEditorOverlay: FunctionComponent = ({ loadedCheckID, view, }) => { - useEffect(() => { - onGetCheckForTimeMachine(checkID) - }, [checkID]) + const dispatch = useDispatch() + const query = get(view, 'properties.queries[0]', null) useEffect(() => { - onExecuteQueries() - }, [get(view, 'properties.queries[0]', null)]) + dispatch(getCheckForTimeMachine(checkID)) + }, [dispatch, checkID]) + + useEffect(() => { + dispatch(executeQueries()) + }, [dispatch, query]) const handleClose = () => { history.push(`/orgs/${orgID}/alerting`) @@ -108,9 +109,7 @@ const mstp = (state: AppState) => { } const mdtp = { - onGetCheckForTimeMachine: getCheckForTimeMachine, onSaveCheckFromTimeMachine: updateCheckFromTimeMachine, - onExecuteQueries: executeQueries, onResetAlertBuilder: resetAlertBuilder, onUpdateAlertBuilderName: updateName, } diff --git a/ui/src/checks/components/NewDeadmanCheckEO.tsx b/ui/src/checks/components/NewDeadmanCheckEO.tsx index ac7e01c34c..ed5f92b7f4 100644 --- a/ui/src/checks/components/NewDeadmanCheckEO.tsx +++ b/ui/src/checks/components/NewDeadmanCheckEO.tsx @@ -1,6 +1,6 @@ // Libraries import React, {FunctionComponent, useEffect} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' import {withRouter, RouteComponentProps} from 'react-router-dom' // Components @@ -34,18 +34,20 @@ const NewCheckOverlay: FunctionComponent = ({ checkName, history, onSaveCheckFromTimeMachine, - onSetActiveTimeMachine, onResetAlertBuilder, onUpdateAlertBuilderName, - onInitializeAlertBuilder, }) => { + const dispatch = useDispatch() + useEffect(() => { const view = createView('deadman') - onInitializeAlertBuilder('deadman') - onSetActiveTimeMachine('alerting', { - view, - }) - }, []) + dispatch(initializeAlertBuilder('deadman')) + dispatch( + setActiveTimeMachine('alerting', { + view, + }) + ) + }, [dispatch]) const handleClose = () => { history.push(`/orgs/${orgID}/alerting`) @@ -80,11 +82,9 @@ const mstp = ({alertBuilder: {name, status}}: AppState) => { } const mdtp = { - onSetActiveTimeMachine: setActiveTimeMachine, onSaveCheckFromTimeMachine: createCheckFromTimeMachine, onResetAlertBuilder: resetAlertBuilder, onUpdateAlertBuilderName: updateName, - onInitializeAlertBuilder: initializeAlertBuilder, } const connector = connect(mstp, mdtp) diff --git a/ui/src/checks/components/NewThresholdCheckEO.tsx b/ui/src/checks/components/NewThresholdCheckEO.tsx index 89b75951d1..23e10e7ad9 100644 --- a/ui/src/checks/components/NewThresholdCheckEO.tsx +++ b/ui/src/checks/components/NewThresholdCheckEO.tsx @@ -1,6 +1,6 @@ // Libraries import React, {FunctionComponent, useEffect} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' import {withRouter, RouteComponentProps} from 'react-router-dom' // Components @@ -34,18 +34,20 @@ const NewCheckOverlay: FunctionComponent = ({ checkName, history, onSaveCheckFromTimeMachine, - onSetActiveTimeMachine, onResetAlertBuilder, onUpdateAlertBuilderName, - onInitializeAlertBuilder, }) => { + const dispatch = useDispatch() + useEffect(() => { const view = createView('threshold') - onInitializeAlertBuilder('threshold') - onSetActiveTimeMachine('alerting', { - view, - }) - }, []) + dispatch(initializeAlertBuilder('threshold')) + dispatch( + setActiveTimeMachine('alerting', { + view, + }) + ) + }, [dispatch]) const handleClose = () => { history.push(`/orgs/${orgID}/alerting`) @@ -80,11 +82,9 @@ const mstp = ({alertBuilder: {name, status}}: AppState) => { } const mdtp = { - onSetActiveTimeMachine: setActiveTimeMachine, onSaveCheckFromTimeMachine: createCheckFromTimeMachine as any, onResetAlertBuilder: resetAlertBuilder, onUpdateAlertBuilderName: updateName, - onInitializeAlertBuilder: initializeAlertBuilder, } const connector = connect(mstp, mdtp) diff --git a/ui/src/cloud/components/OrgSettings.tsx b/ui/src/cloud/components/OrgSettings.tsx index 590688fc21..3bcd91746b 100644 --- a/ui/src/cloud/components/OrgSettings.tsx +++ b/ui/src/cloud/components/OrgSettings.tsx @@ -1,6 +1,6 @@ // Libraries -import {FunctionComponent, useEffect, useState} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {FC, useEffect, useState} from 'react' +import {connect, ConnectedProps, useDispatch} from 'react-redux' // Constants import {CLOUD} from 'src/shared/constants' @@ -22,21 +22,18 @@ interface PassedInProps { type ReduxProps = ConnectedProps type Props = ReduxProps & PassedInProps -const OrgSettings: FunctionComponent = ({ - org, - getOrgSettings, - settings, - children, -}) => { +const OrgSettings: FC = ({org, settings, children}) => { + const dispatch = useDispatch() const [hasFetchedOrgSettings, setHasFetchedOrgSettings] = useState( false ) + useEffect(() => { if (CLOUD && org && !hasFetchedOrgSettings) { setHasFetchedOrgSettings(true) - getOrgSettings() + dispatch(getOrgSettingsAction()) } - }, [org]) + }, [dispatch, org, hasFetchedOrgSettings]) useEffect(() => { updateReportingContext( diff --git a/ui/src/cloud/utils/reporting.ts b/ui/src/cloud/utils/reporting.ts index d42ea195b2..8f983e41ec 100644 --- a/ui/src/cloud/utils/reporting.ts +++ b/ui/src/cloud/utils/reporting.ts @@ -166,5 +166,5 @@ export const useLoadTimeReporting = (event: string) => { fields: {}, tags: {event}, }) - }, []) + }, [event, loadStartTime]) } diff --git a/ui/src/dashboards/components/DashboardContainer.tsx b/ui/src/dashboards/components/DashboardContainer.tsx index e40265f206..1bc766081e 100644 --- a/ui/src/dashboards/components/DashboardContainer.tsx +++ b/ui/src/dashboards/components/DashboardContainer.tsx @@ -1,6 +1,6 @@ // Libraries import React, {FC, useEffect} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' // Components import GetResource from 'src/resources/components/GetResource' @@ -26,11 +26,8 @@ const {Active} = AutoRefreshStatus type ReduxProps = ConnectedProps type Props = ReduxProps -const DashboardContainer: FC = ({ - autoRefresh, - dashboard, - onSetCurrentPage, -}) => { +const DashboardContainer: FC = ({autoRefresh, dashboard}) => { + const dispatch = useDispatch() useEffect(() => { if (autoRefresh.status === Active) { GlobalAutoRefresher.poll(autoRefresh.interval) @@ -45,11 +42,11 @@ const DashboardContainer: FC = ({ }, [autoRefresh.status, autoRefresh.interval]) useEffect(() => { - onSetCurrentPage('dashboard') + dispatch(setCurrentPage('dashboard')) return () => { - onSetCurrentPage('not set') + dispatch(setCurrentPage('not set')) } - }, []) + }, [dispatch]) return ( @@ -72,10 +69,6 @@ const mstp = (state: AppState) => { } } -const mdtp = { - onSetCurrentPage: setCurrentPage, -} - -const connector = connect(mstp, mdtp) +const connector = connect(mstp) export default connector(DashboardContainer) diff --git a/ui/src/dashboards/components/DashboardHeader.tsx b/ui/src/dashboards/components/DashboardHeader.tsx index c9a0c976e9..44e6806903 100644 --- a/ui/src/dashboards/components/DashboardHeader.tsx +++ b/ui/src/dashboards/components/DashboardHeader.tsx @@ -74,7 +74,7 @@ const DashboardHeader: FC = ({ }) => { useEffect(() => { fireDashboardViewedEvent(dashboard.name) - }, [dashboard.id]) + }, [dashboard.id]) // eslint-disable-line react-hooks/exhaustive-deps const handleAddNote = () => { history.push(`/orgs/${org.id}/dashboards/${dashboard.id}/notes/new`) diff --git a/ui/src/dashboards/components/EditVEO.tsx b/ui/src/dashboards/components/EditVEO.tsx index 85537b8e08..64061442fc 100644 --- a/ui/src/dashboards/components/EditVEO.tsx +++ b/ui/src/dashboards/components/EditVEO.tsx @@ -1,7 +1,7 @@ // Libraries import React, {FunctionComponent, useEffect} from 'react' import {withRouter, RouteComponentProps} from 'react-router-dom' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' import {get} from 'lodash' // Components @@ -26,7 +26,6 @@ type Props = ReduxProps & const EditViewVEO: FunctionComponent = ({ activeTimeMachineID, - getViewAndResultsForVEO, onSaveView, onSetName, match: { @@ -35,12 +34,13 @@ const EditViewVEO: FunctionComponent = ({ history, view, }) => { + const dispatch = useDispatch() useEffect(() => { // TODO split this up into "loadView" "setActiveTimeMachine" // and something to tell the component to pull from the context // of the dashboardID - getViewAndResultsForVEO(dashboardID, cellID, 'veo') - }, []) + dispatch(getViewAndResultsForVEO(dashboardID, cellID, 'veo')) + }, [dispatch, dashboardID, cellID]) const handleClose = () => { history.push(`/orgs/${orgID}/dashboards/${dashboardID}`) @@ -91,7 +91,6 @@ const mstp = (state: AppState) => { } const mdtp = { - getViewAndResultsForVEO: getViewAndResultsForVEO, onSetName: setName, onSaveView: saveVEOView, } diff --git a/ui/src/dashboards/components/GetTimeRange.tsx b/ui/src/dashboards/components/GetTimeRange.tsx index 2ac27e30d4..7bf0f05dc3 100644 --- a/ui/src/dashboards/components/GetTimeRange.tsx +++ b/ui/src/dashboards/components/GetTimeRange.tsx @@ -1,25 +1,20 @@ // Libraries import React, {FC, useEffect} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {useDispatch, useSelector} from 'react-redux' import {withRouter, RouteComponentProps} from 'react-router-dom' import {getTimeRange} from 'src/dashboards/selectors' // Actions -import * as actions from 'src/dashboards/actions/ranges' - -// Types -import {AppState} from 'src/types' - -type ReduxProps = ConnectedProps -type Props = RouteComponentProps<{dashboardID: string}> & ReduxProps - -const GetTimeRange: FC = ({ - location, - match, - timeRange, +import { setDashboardTimeRange, updateQueryParams, -}: Props) => { +} from 'src/dashboards/actions/ranges' + +type Props = RouteComponentProps<{dashboardID: string}> + +const GetTimeRange: FC = ({location, match}: Props) => { + const dispatch = useDispatch() + const timeRange = useSelector(getTimeRange) const isEditing = location.pathname.includes('edit') const isNew = location.pathname.includes('new') @@ -29,27 +24,17 @@ const GetTimeRange: FC = ({ } // TODO: map this to current contextID - setDashboardTimeRange(match.params.dashboardID, timeRange) + dispatch(setDashboardTimeRange(match.params.dashboardID, timeRange)) const {lower, upper} = timeRange - updateQueryParams({ - lower, - upper, - }) - }, [isEditing, isNew]) + dispatch( + updateQueryParams({ + lower, + upper, + }) + ) + }, [dispatch, isEditing, isNew, match.params.dashboardID, timeRange]) return
} -const mstp = (state: AppState) => { - const timeRange = getTimeRange(state) - return {timeRange} -} - -const mdtp = { - updateQueryParams: actions.updateQueryParams, - setDashboardTimeRange: actions.setDashboardTimeRange, -} - -const connector = connect(mstp, mdtp) - -export default withRouter(connector(GetTimeRange)) +export default withRouter(GetTimeRange) diff --git a/ui/src/dashboards/components/NewVEO.tsx b/ui/src/dashboards/components/NewVEO.tsx index 4078052ce2..9f8351b7e4 100644 --- a/ui/src/dashboards/components/NewVEO.tsx +++ b/ui/src/dashboards/components/NewVEO.tsx @@ -1,7 +1,7 @@ // Libraries import React, {FunctionComponent, useEffect} from 'react' import {withRouter, RouteComponentProps} from 'react-router-dom' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' import {get} from 'lodash' // Components @@ -26,7 +26,6 @@ type Props = ReduxProps & const NewViewVEO: FunctionComponent = ({ activeTimeMachineID, - onLoadNewVEO, onSaveView, onSetName, match: { @@ -35,9 +34,10 @@ const NewViewVEO: FunctionComponent = ({ history, view, }) => { + const dispatch = useDispatch() useEffect(() => { - onLoadNewVEO() - }, [dashboardID]) + dispatch(loadNewVEO()) + }, [dispatch, dashboardID]) const handleClose = () => { history.push(`/orgs/${orgID}/dashboards/${dashboardID}`) @@ -47,7 +47,9 @@ const NewViewVEO: FunctionComponent = ({ try { onSaveView(dashboardID) handleClose() - } catch (e) {} + } catch (error) { + console.error(error) + } } let loadingState = RemoteDataState.Loading @@ -89,7 +91,6 @@ const mstp = (state: AppState) => { const mdtp = { onSetName: setName, onSaveView: saveVEOView, - onLoadNewVEO: loadNewVEO, } const connector = connect(mstp, mdtp) diff --git a/ui/src/dataExplorer/components/DataExplorer.tsx b/ui/src/dataExplorer/components/DataExplorer.tsx index 59d00f8ae9..317089af06 100644 --- a/ui/src/dataExplorer/components/DataExplorer.tsx +++ b/ui/src/dataExplorer/components/DataExplorer.tsx @@ -1,6 +1,6 @@ // Libraries import React, {FC, useEffect} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {useDispatch} from 'react-redux' // Components import TimeMachine from 'src/timeMachine/components/TimeMachine' @@ -16,19 +16,15 @@ import {HoverTimeProvider} from 'src/dashboards/utils/hoverTime' import {queryBuilderFetcher} from 'src/timeMachine/apis/QueryBuilderFetcher' import {readQueryParams} from 'src/shared/utils/queryParams' -type ReduxProps = ConnectedProps -type Props = ReduxProps +const DataExplorer: FC = () => { + const dispatch = useDispatch() -const DataExplorer: FC = ({ - onSetActiveTimeMachine, - onSetBuilderBucketIfExists, -}) => { useEffect(() => { const bucketQP = readQueryParams()['bucket'] - onSetActiveTimeMachine('de') + dispatch(setActiveTimeMachine('de')) queryBuilderFetcher.clearCache() - onSetBuilderBucketIfExists(bucketQP) - }, []) + dispatch(setBuilderBucketIfExists(bucketQP)) + }, [dispatch]) return ( @@ -42,11 +38,4 @@ const DataExplorer: FC = ({ ) } -const mdtp = { - onSetActiveTimeMachine: setActiveTimeMachine, - onSetBuilderBucketIfExists: setBuilderBucketIfExists, -} - -const connector = connect(null, mdtp) - -export default connector(DataExplorer) +export default DataExplorer diff --git a/ui/src/dataExplorer/components/DeleteDataOverlay.tsx b/ui/src/dataExplorer/components/DeleteDataOverlay.tsx index 39cc7ac51e..8042017fc9 100644 --- a/ui/src/dataExplorer/components/DeleteDataOverlay.tsx +++ b/ui/src/dataExplorer/components/DeleteDataOverlay.tsx @@ -1,6 +1,6 @@ // Libraries import React, {FunctionComponent, useEffect} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {connect, ConnectedProps, useDispatch} from 'react-redux' import {withRouter, RouteComponentProps} from 'react-router-dom' import {get} from 'lodash' @@ -33,24 +33,23 @@ const DeleteDataOverlay: FunctionComponent = ({ }, bucketNameFromDE, timeRangeFromDE, - resetPredicateState, - setTimeRange, - setBucketAndKeys, }) => { + const dispatch = useDispatch() + useEffect(() => { if (bucketNameFromDE) { - setBucketAndKeys(bucketNameFromDE) + dispatch(setBucketAndKeys(bucketNameFromDE)) } - }, [bucketNameFromDE]) + }, [dispatch, bucketNameFromDE]) useEffect(() => { if (timeRangeFromDE) { - setTimeRange(convertTimeRangeToCustom(timeRangeFromDE)) + dispatch(setTimeRange(convertTimeRangeToCustom(timeRangeFromDE))) } - }, [timeRangeFromDE]) + }, [dispatch, timeRangeFromDE]) const handleDismiss = () => { - resetPredicateState() + dispatch(resetPredicateState()) history.push(`/orgs/${orgID}/data-explorer`) } @@ -80,12 +79,6 @@ const mstp = (state: AppState) => { } } -const mdtp = { - resetPredicateState, - setTimeRange, - setBucketAndKeys, -} - -const connector = connect(mstp, mdtp) +const connector = connect(mstp) export default connector(withRouter(DeleteDataOverlay)) diff --git a/ui/src/eventViewer/components/EventTable.tsx b/ui/src/eventViewer/components/EventTable.tsx index 32080fa887..a7913ad7c5 100644 --- a/ui/src/eventViewer/components/EventTable.tsx +++ b/ui/src/eventViewer/components/EventTable.tsx @@ -1,6 +1,6 @@ // Libraries import React, {useLayoutEffect, FC, useEffect, useState} from 'react' -import {connect, ConnectedProps} from 'react-redux' +import {useDispatch} from 'react-redux' import {AutoSizer, InfiniteLoader, List} from 'react-virtualized' // Components @@ -11,7 +11,7 @@ import FooterRow from 'src/eventViewer/components/FooterRow' import ErrorRow from 'src/eventViewer/components/ErrorRow' // Actions -import {notify as notifyAction} from 'src/shared/actions/notifications' +import {notify} from 'src/shared/actions/notifications' // Utils import { @@ -30,13 +30,14 @@ type OwnProps = { fields: Fields } -type ReduxProps = ConnectedProps -type Props = EventViewerChildProps & ReduxProps & OwnProps +type Props = EventViewerChildProps & OwnProps -const EventTable: FC = ({state, dispatch, loadRows, fields, notify}) => { +const rowLoadedFn = state => ({index}) => !!state.rows[index] + +const EventTable: FC = ({state, dispatch, loadRows, fields}) => { const rowCount = getRowCount(state) - const isRowLoaded = ({index}) => !!state.rows[index] + const isRowLoaded = rowLoadedFn(state) const isRowLoadedBoolean = !!state.rows[0] @@ -44,6 +45,8 @@ const EventTable: FC = ({state, dispatch, loadRows, fields, notify}) => { const [isLongRunningQuery, setIsLongRunningQuery] = useState(false) + const reduxDispatch = useDispatch() + useEffect(() => { setTimeout(() => { setIsLongRunningQuery(true) @@ -52,9 +55,9 @@ const EventTable: FC = ({state, dispatch, loadRows, fields, notify}) => { useEffect(() => { if (isLongRunningQuery && !isRowLoadedBoolean) { - notify(checkStatusLoading) + reduxDispatch(notify(checkStatusLoading)) } - }, [isLongRunningQuery, isRowLoaded]) + }, [reduxDispatch, isLongRunningQuery, isRowLoadedBoolean, isRowLoaded]) const rowRenderer = ({key, index, style}) => { const isLastRow = index === state.rows.length @@ -129,10 +132,4 @@ const EventTable: FC = ({state, dispatch, loadRows, fields, notify}) => { ) } -const mdtp = { - notify: notifyAction, -} - -const connector = connect(null, mdtp) - -export default connector(EventTable) +export default EventTable diff --git a/ui/src/eventViewer/components/EventViewer.tsx b/ui/src/eventViewer/components/EventViewer.tsx index a1b400f775..8bf94f2752 100644 --- a/ui/src/eventViewer/components/EventViewer.tsx +++ b/ui/src/eventViewer/components/EventViewer.tsx @@ -28,13 +28,17 @@ const EventViewer: FC = ({loadRows, children, initialState}) => { useEffect(() => { loadNextRows(state, dispatch, loadRows, Date.now()) - }, []) + }, []) // eslint-disable-line react-hooks/exhaustive-deps useMountedLayoutEffect(() => { refresh(state, dispatch, loadRows) }, [loadRows]) - return children({state, dispatch, loadRows}) + return children({ + state, + dispatch, + loadRows, + }) } export default EventViewer diff --git a/ui/src/me/components/LogoutButton.tsx b/ui/src/me/components/LogoutButton.tsx index 015dd10854..92effc7933 100644 --- a/ui/src/me/components/LogoutButton.tsx +++ b/ui/src/me/components/LogoutButton.tsx @@ -8,7 +8,11 @@ import {Button, ComponentSize} from '@influxdata/clockface' const LogoutButton: SFC = () => ( <> -