From c3673df6e1887cdb2626d499d611367128afda81 Mon Sep 17 00:00:00 2001 From: czgu Date: Wed, 1 Feb 2023 09:53:58 -0800 Subject: [PATCH] chore: Move QueryViewNavigator to hooks (#1140) --- .../QueryViewNavigator/QueryViewNavigator.tsx | 279 +++++++++--------- 1 file changed, 135 insertions(+), 144 deletions(-) diff --git a/querybook/webapp/components/QueryViewNavigator/QueryViewNavigator.tsx b/querybook/webapp/components/QueryViewNavigator/QueryViewNavigator.tsx index bd97e1a08..0e374499c 100644 --- a/querybook/webapp/components/QueryViewNavigator/QueryViewNavigator.tsx +++ b/querybook/webapp/components/QueryViewNavigator/QueryViewNavigator.tsx @@ -1,8 +1,9 @@ -import { bind, debounce } from 'lodash-decorators'; -import React from 'react'; -import { connect } from 'react-redux'; +import { debounce } from 'lodash'; +import React, { useCallback, useEffect, useRef } from 'react'; +import { useDispatch } from 'react-redux'; import { IQueryExecution, QueryExecutionStatus } from 'const/queryExecution'; +import { useShallowSelector } from 'hooks/redux/useShallowSelector'; import { queryEngineByIdEnvSelector, queryEngineSelector, @@ -10,7 +11,7 @@ import { import * as queryExecutionsActions from 'redux/queryExecutions/action'; import * as queryViewActions from 'redux/queryView/action'; import { queryExecutionResultSelector } from 'redux/queryView/selector'; -import { Dispatch, IStoreState } from 'redux/store/types'; +import { IStoreState } from 'redux/store/types'; import { Icon } from 'ui/Icon/Icon'; import { AccentText } from 'ui/StyledText/StyledText'; @@ -19,148 +20,138 @@ import { QueryViewFilter } from './QueryViewFilter'; import './QueryViewNavigator.scss'; -type StateProps = ReturnType; -type DispatchProps = ReturnType; - -export type IProps = StateProps & DispatchProps; - -class QueryViewNavigatorComponent extends React.PureComponent { - private navigatorScrollRef = React.createRef(); - - public constructor(props) { - super(props); - - this.state = { - showQueryViewModalForId: null, - }; - } - - @bind - public setupPolling(queryExecutions: IQueryExecution[]) { - for (const queryExecution of queryExecutions) { - if (queryExecution.status < QueryExecutionStatus.DONE) { - this.props.pollQueryExecution(queryExecution.id); +export const QueryViewNavigator: React.FC = () => { + const { + queryResults, + isLoadingQueries, + queryViewFilters, + queryEngines, + queryEngineById, + } = useShallowSelector((state: IStoreState) => ({ + queryResults: queryExecutionResultSelector(state), + isLoadingQueries: state.queryView.isLoading, + queryViewFilters: state.queryView.filters, + queryEngines: queryEngineSelector(state), + queryEngineById: queryEngineByIdEnvSelector(state), + })); + + const dispatch = useDispatch(); + const updateFilter = useCallback( + (filterKey: string, filterValue: any) => { + dispatch(queryViewActions.updateFilter(filterKey, filterValue)); + }, + [dispatch] + ); + + const initializeFromQueryParam = useCallback( + () => dispatch(queryViewActions.mapQueryParamToState()), + [dispatch] + ); + + const loadQueries = useCallback( + () => dispatch(queryViewActions.searchQueries()), + [dispatch] + ); + + const pollQueryExecution = useCallback( + (queryExecutionId: number) => { + dispatch( + queryExecutionsActions.pollQueryExecution(queryExecutionId) + ); + }, + [dispatch] + ); + + const navigatorScrollRef = useRef(); + + const setupPolling = useCallback( + (queryExecutions: IQueryExecution[]) => { + for (const queryExecution of queryExecutions) { + if (queryExecution.status < QueryExecutionStatus.DONE) { + pollQueryExecution(queryExecution.id); + } } - } - } - - @bind - public onNavigatorScroll(event) { - if (event.target === this.navigatorScrollRef.current) { - this.loadMoreIfScrolledToBottom(); - } - } - - @bind - @debounce(500) - public loadMoreIfScrolledToBottom() { - const { isLoadingQueries, loadQueries } = this.props; - - if (!isLoadingQueries && this.navigatorScrollRef) { - const el = this.navigatorScrollRef.current; - if (el.scrollHeight - el.scrollTop === el.clientHeight) { - // Scrolled to bottom - loadQueries(); + }, + [pollQueryExecution] + ); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const loadMoreIfScrolledToBottom = useCallback( + debounce(() => { + if (!isLoadingQueries && navigatorScrollRef) { + const el = navigatorScrollRef.current; + if (el.scrollHeight - el.scrollTop === el.clientHeight) { + // Scrolled to bottom + loadQueries(); + } } - } - } - - public componentDidMount() { - this.props.initializeFromQueryParam(); - this.setupPolling(this.props.queryResults); - } - - public componentDidUpdate(prevProps) { - if (this.props.queryResults !== prevProps.queryResults) { - this.setupPolling(this.props.queryResults); - } - } - - public render() { - const { - queryResults, - isLoadingQueries, - queryViewFilters, - queryEngines, - queryEngineById, - - initializeFromQueryParam, - updateFilter, - } = this.props; - - const queryViewFilterDOM = ( - - ); - - const queryResultsListDOM = queryResults.map((queryResult) => ( - - )); - - const loadingDOM = isLoadingQueries ? ( -
- - - Loading Executions - -
+ }, 500), + [loadQueries, isLoadingQueries] + ); + + const handleNavigatorScroll = useCallback( + (event) => { + if (event.target === navigatorScrollRef.current) { + loadMoreIfScrolledToBottom(); + } + }, + [loadMoreIfScrolledToBottom] + ); + + useEffect(() => { + initializeFromQueryParam(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + setupPolling(queryResults); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [queryResults]); + + const queryViewFilterDOM = ( + + ); + + const queryResultsListDOM = queryResults.map((queryResult) => ( + + )); + + const loadingDOM = isLoadingQueries ? ( +
+ + + Loading Executions + +
+ ) : null; + + const noResultDOM = + queryResults.length === 0 && !loadingDOM ? ( +
No Executions
) : null; - const noResultDOM = - queryResults.length === 0 && !loadingDOM ? ( -
No Executions
- ) : null; - - return ( -
-
{queryViewFilterDOM}
-
- {queryResultsListDOM} - {loadingDOM} - {noResultDOM} -
+ return ( +
+
{queryViewFilterDOM}
+
+ {queryResultsListDOM} + {loadingDOM} + {noResultDOM}
- ); - } -} - -const mapStateToProps = (state: IStoreState) => ({ - queryResults: queryExecutionResultSelector(state), - isLoadingQueries: state.queryView.isLoading, - queryViewFilters: state.queryView.filters, - queryEngines: queryEngineSelector(state), - queryEngineById: queryEngineByIdEnvSelector(state), -}); - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - updateFilter: (filterKey: string, filterValue: any) => { - dispatch(queryViewActions.updateFilter(filterKey, filterValue)); - }, - - initializeFromQueryParam: () => - dispatch(queryViewActions.mapQueryParamToState()), - - loadQueries: () => dispatch(queryViewActions.searchQueries()), - - pollQueryExecution: (queryExecutionId) => { - dispatch(queryExecutionsActions.pollQueryExecution(queryExecutionId)); - }, -}); - -export const QueryViewNavigator = connect( - mapStateToProps, - mapDispatchToProps -)(QueryViewNavigatorComponent); +
+ ); +};