From 245050aac27a0812905c73c9ccce3a685fde0888 Mon Sep 17 00:00:00 2001 From: Nidhi Tandon <> Date: Sat, 22 May 2021 17:26:16 +0530 Subject: [PATCH 1/5] fix(ServiceMap): multiple api calls of date picker --- frontend/src/modules/AppWrapper.tsx | 8 +++++++- frontend/src/modules/Servicemap/ServiceMap.tsx | 14 +++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/frontend/src/modules/AppWrapper.tsx b/frontend/src/modules/AppWrapper.tsx index b261ad3e32..17442bd495 100644 --- a/frontend/src/modules/AppWrapper.tsx +++ b/frontend/src/modules/AppWrapper.tsx @@ -34,7 +34,13 @@ const App = () => { - + ( + + )} + /> diff --git a/frontend/src/modules/Servicemap/ServiceMap.tsx b/frontend/src/modules/Servicemap/ServiceMap.tsx index 048c6980b9..1e0ac23fba 100644 --- a/frontend/src/modules/Servicemap/ServiceMap.tsx +++ b/frontend/src/modules/Servicemap/ServiceMap.tsx @@ -1,6 +1,6 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect, useRef } from "react"; import { connect } from "react-redux"; -import { RouteComponentProps } from "react-router-dom"; +import { RouteComponentProps, useLocation } from "react-router-dom"; import { GlobalTime, serviceMapStore, @@ -36,6 +36,7 @@ interface ServiceMapProps extends RouteComponentProps { globalTime: GlobalTime; getServiceMapItems: Function; getDetailedServiceMapItems: Function; + componentPath: string; } interface graphNode { id: string; @@ -53,16 +54,19 @@ export interface graphDataType { const ServiceMap = (props: ServiceMapProps) => { const fgRef = useRef(); + const location = useLocation(); const { getDetailedServiceMapItems, getServiceMapItems, globalTime, serviceMap, + componentPath, } = props; - useEffect(() => { - getServiceMapItems(globalTime); - getDetailedServiceMapItems(globalTime); + if (location.pathname === componentPath) { + getServiceMapItems(globalTime); + getDetailedServiceMapItems(globalTime); + } }, [globalTime]); useEffect(() => { From 44495b76691e64929d77c5d55a43eb8698ab2797 Mon Sep 17 00:00:00 2001 From: Nidhi Tandon <> Date: Sun, 23 May 2021 14:15:13 +0530 Subject: [PATCH 2/5] feat(ServiceMap): dispatch isLoaded route via context --- frontend/src/modules/AppWrapper.tsx | 69 +++++++++---------- frontend/src/modules/BaseLayout.tsx | 42 ++++++++++- frontend/src/modules/RouteProvider.tsx | 51 ++++++++++++++ .../src/modules/Servicemap/ServiceMap.tsx | 12 ++-- 4 files changed, 132 insertions(+), 42 deletions(-) create mode 100644 frontend/src/modules/RouteProvider.tsx diff --git a/frontend/src/modules/AppWrapper.tsx b/frontend/src/modules/AppWrapper.tsx index 17442bd495..11e53cd04d 100644 --- a/frontend/src/modules/AppWrapper.tsx +++ b/frontend/src/modules/AppWrapper.tsx @@ -17,6 +17,7 @@ import { SettingsPage, IntstrumentationPage, } from "Src/pages"; +import { RouteProvider } from "./RouteProvider"; const App = () => { const { status } = useThemeSwitcher(); @@ -30,42 +31,38 @@ const App = () => { }> - - - - - ( - - )} - /> - - - - - - { - return localStorage.getItem(IS_LOGGED_IN) === "yes" ? ( - - ) : ( - - ); - }} - /> - + + + + + + + + + + + + { + return localStorage.getItem(IS_LOGGED_IN) === "yes" ? ( + + ) : ( + + ); + }} + /> + + diff --git a/frontend/src/modules/BaseLayout.tsx b/frontend/src/modules/BaseLayout.tsx index a36ac96e66..43df370f49 100644 --- a/frontend/src/modules/BaseLayout.tsx +++ b/frontend/src/modules/BaseLayout.tsx @@ -1,15 +1,55 @@ -import React, { ReactNode } from "react"; +import React, { ReactNode, useEffect } from "react"; import { Layout } from "antd"; import SideNav from "./Nav/SideNav"; import TopNav from "./Nav/TopNav"; +import { useLocation } from "react-router-dom"; +import ROUTES from "Src/constants/routes"; +import { useRoute } from "./RouteProvider"; + const { Content, Footer } = Layout; interface BaseLayoutProps { children: ReactNode; } +interface RouteObj { + [key: string]: { + route: string; + isLoaded: boolean; + }; +} const BaseLayout: React.FC = ({ children }) => { + const location = useLocation(); + const { dispatch } = useRoute(); + + /* + Create a routes obj with values as + { + SERVICE_MAP: { + route: '/service-map', + isLoaded: false + } + */ + const routes: RouteObj = {}; + Object.keys(ROUTES).map((items) => { + routes[items] = { + route: `${ROUTES[items]}`, + isLoaded: false, + }; + }); + + useEffect(() => { + /* + Update the isLoaded property in routes obj + if the route matches the current pathname + */ + Object.keys(ROUTES).map((items) => { + routes[items].isLoaded = routes[items].route === location.pathname; + }); + dispatch({ type: "UPDATE", payload: routes }); + }, [location]); + return ( diff --git a/frontend/src/modules/RouteProvider.tsx b/frontend/src/modules/RouteProvider.tsx new file mode 100644 index 0000000000..2894e8fe3f --- /dev/null +++ b/frontend/src/modules/RouteProvider.tsx @@ -0,0 +1,51 @@ +import React, { useContext, createContext, ReactNode, Dispatch } from "react"; + +type State = { + [key: string]: { + route: string; + isLoaded: boolean; + }; +}; + +type Action = { + type: "UPDATE"; + payload: State; +}; + +interface ContextType { + state: State; + dispatch: Dispatch; +} + +const RouteContext = createContext(null); + +interface RouteProviderProps { + children: ReactNode; +} + +const updateLocation = (state: State, action: Action): State => { + if (action.type === "UPDATE") { + return { + ...state, + ...action.payload, + }; + } + return { + ...state, + }; +}; + +const RouteProvider: React.FC = ({ children }) => { + const [state, dispatch] = React.useReducer(updateLocation, {}); + const value = { state, dispatch }; + return {children}; +}; + +const useRoute = (): ContextType => { + const context = useContext(RouteContext); + if (context === undefined) { + throw new Error("useRoute must be used within a RouteProvider"); + } + return context as ContextType; +}; +export { RouteProvider, useRoute }; diff --git a/frontend/src/modules/Servicemap/ServiceMap.tsx b/frontend/src/modules/Servicemap/ServiceMap.tsx index 1e0ac23fba..8306a35ada 100644 --- a/frontend/src/modules/Servicemap/ServiceMap.tsx +++ b/frontend/src/modules/Servicemap/ServiceMap.tsx @@ -1,6 +1,6 @@ -import React, { useEffect, useRef } from "react"; +import React, { useContext, useEffect, useRef } from "react"; import { connect } from "react-redux"; -import { RouteComponentProps, useLocation } from "react-router-dom"; +import { RouteComponentProps } from "react-router-dom"; import { GlobalTime, serviceMapStore, @@ -14,6 +14,7 @@ import { StoreState } from "../../store/reducers"; import { getZoomPx, getGraphData, getTooltip, transformLabel } from "./utils"; import SelectService from "./SelectService"; import { ForceGraph2D } from "react-force-graph"; +import { useRoute } from "../RouteProvider"; const Container = styled.div` .force-graph-container .graph-tooltip { @@ -54,16 +55,17 @@ export interface graphDataType { const ServiceMap = (props: ServiceMapProps) => { const fgRef = useRef(); - const location = useLocation(); + const { state } = useRoute(); + const { getDetailedServiceMapItems, getServiceMapItems, globalTime, serviceMap, - componentPath, } = props; + useEffect(() => { - if (location.pathname === componentPath) { + if (state.SERVICE_MAP.isLoaded) { getServiceMapItems(globalTime); getDetailedServiceMapItems(globalTime); } From e4b41b1a276a51b1962ded234755e6bcb93ce12a Mon Sep 17 00:00:00 2001 From: Nidhi Tandon <> Date: Sun, 23 May 2021 15:40:48 +0530 Subject: [PATCH 3/5] feat: load data based on isLoaded flag --- frontend/src/modules/BaseLayout.tsx | 31 +------------- frontend/src/modules/RouteProvider.tsx | 42 ++++++++++++++++--- .../Traces/TraceCustomVisualizations.tsx | 8 +++- frontend/src/modules/Traces/TraceFilter.tsx | 6 ++- frontend/src/modules/Usage/UsageExplorer.tsx | 7 +++- 5 files changed, 56 insertions(+), 38 deletions(-) diff --git a/frontend/src/modules/BaseLayout.tsx b/frontend/src/modules/BaseLayout.tsx index 43df370f49..6e3f52769e 100644 --- a/frontend/src/modules/BaseLayout.tsx +++ b/frontend/src/modules/BaseLayout.tsx @@ -13,41 +13,12 @@ interface BaseLayoutProps { children: ReactNode; } -interface RouteObj { - [key: string]: { - route: string; - isLoaded: boolean; - }; -} const BaseLayout: React.FC = ({ children }) => { const location = useLocation(); const { dispatch } = useRoute(); - /* - Create a routes obj with values as - { - SERVICE_MAP: { - route: '/service-map', - isLoaded: false - } - */ - const routes: RouteObj = {}; - Object.keys(ROUTES).map((items) => { - routes[items] = { - route: `${ROUTES[items]}`, - isLoaded: false, - }; - }); - useEffect(() => { - /* - Update the isLoaded property in routes obj - if the route matches the current pathname - */ - Object.keys(ROUTES).map((items) => { - routes[items].isLoaded = routes[items].route === location.pathname; - }); - dispatch({ type: "UPDATE", payload: routes }); + dispatch({ type: "UPDATE_IS_LOADED", payload: location.pathname }); }, [location]); return ( diff --git a/frontend/src/modules/RouteProvider.tsx b/frontend/src/modules/RouteProvider.tsx index 2894e8fe3f..0680e912c8 100644 --- a/frontend/src/modules/RouteProvider.tsx +++ b/frontend/src/modules/RouteProvider.tsx @@ -1,4 +1,5 @@ import React, { useContext, createContext, ReactNode, Dispatch } from "react"; +import ROUTES from "Src/constants/routes"; type State = { [key: string]: { @@ -7,9 +8,13 @@ type State = { }; }; +enum ActionTypes { + UPDATE_IS_LOADED = "UPDATE_IS_LOADED", +} + type Action = { - type: "UPDATE"; - payload: State; + type: ActionTypes; + payload: string; }; interface ContextType { @@ -22,12 +27,28 @@ const RouteContext = createContext(null); interface RouteProviderProps { children: ReactNode; } +interface RouteObj { + [key: string]: { + route: string; + isLoaded: boolean; + }; +} const updateLocation = (state: State, action: Action): State => { - if (action.type === "UPDATE") { + if (action.type === ActionTypes.UPDATE_IS_LOADED) { + /* + Update the isLoaded property in routes obj + if the route matches the current pathname + + Why: Checkout this issue https://github.com/SigNoz/signoz/issues/110 + To avoid calling the api's twice for Date picker, + We will only call once the route is changed + */ + Object.keys(ROUTES).map((items) => { + state[items].isLoaded = state[items].route === action.payload; + }); return { ...state, - ...action.payload, }; } return { @@ -35,8 +56,19 @@ const updateLocation = (state: State, action: Action): State => { }; }; +const getInitialState = () => { + const routes: RouteObj = {}; + Object.keys(ROUTES).map((items) => { + routes[items] = { + route: `${ROUTES[items]}`, + isLoaded: false, + }; + }); + return routes; +}; + const RouteProvider: React.FC = ({ children }) => { - const [state, dispatch] = React.useReducer(updateLocation, {}); + const [state, dispatch] = React.useReducer(updateLocation, getInitialState()); const value = { state, dispatch }; return {children}; }; diff --git a/frontend/src/modules/Traces/TraceCustomVisualizations.tsx b/frontend/src/modules/Traces/TraceCustomVisualizations.tsx index 3c81ad244b..7b26ce4a29 100644 --- a/frontend/src/modules/Traces/TraceCustomVisualizations.tsx +++ b/frontend/src/modules/Traces/TraceCustomVisualizations.tsx @@ -10,6 +10,7 @@ import { GlobalTime, TraceFilters, } from "../../store/actions"; +import { useRoute } from "../RouteProvider"; const { Option } = Select; @@ -81,6 +82,8 @@ const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => { const [selectedEntity, setSelectedEntity] = useState("calls"); const [selectedAggOption, setSelectedAggOption] = useState("count"); const [selectedStep, setSelectedStep] = useState("60"); + const { state } = useRoute(); + // Step should be multiples of 60, 60 -> 1 min useEffect(() => { @@ -109,7 +112,10 @@ const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => { minTime: props.globalTime.minTime - 15 * 60 * 1000000000, maxTime: props.globalTime.maxTime + 15 * 60 * 1000000000, }; - props.getFilteredTraceMetrics(request_string, plusMinus15); + + if (state.TRACES.isLoaded) { + props.getFilteredTraceMetrics(request_string, plusMinus15); + } }, [selectedEntity, selectedAggOption, props.traceFilters, props.globalTime]); //Custom metrics API called if time, tracefilters, selected entity or agg option changes diff --git a/frontend/src/modules/Traces/TraceFilter.tsx b/frontend/src/modules/Traces/TraceFilter.tsx index 839ef64d12..06e69f6fd0 100644 --- a/frontend/src/modules/Traces/TraceFilter.tsx +++ b/frontend/src/modules/Traces/TraceFilter.tsx @@ -18,6 +18,7 @@ import FormItem from "antd/lib/form/FormItem"; import api, { apiV1 } from "../../api"; import { useLocation } from "react-router-dom"; import { METRICS_PAGE_QUERY_PARAM } from "Src/constants/query"; +import { useRoute } from "../RouteProvider"; const { Option } = Select; @@ -45,6 +46,7 @@ const _TraceFilter = (props: TraceFilterProps) => { const [tagKeyOptions, setTagKeyOptions] = useState([]); const location = useLocation(); const urlParams = new URLSearchParams(location.search.split("?")[1]); + const { state } = useRoute(); useEffect(() => { handleApplyFilterForm({ @@ -122,7 +124,9 @@ const _TraceFilter = (props: TraceFilterProps) => { "&tags=" + encodeURIComponent(JSON.stringify(props.traceFilters.tags)); - props.fetchTraces(props.globalTime, request_string); + if (state.TRACES.isLoaded) { + props.fetchTraces(props.globalTime, request_string); + } }, [props.traceFilters, props.globalTime]); useEffect(() => { diff --git a/frontend/src/modules/Usage/UsageExplorer.tsx b/frontend/src/modules/Usage/UsageExplorer.tsx index 3152e12693..1e1675e0db 100644 --- a/frontend/src/modules/Usage/UsageExplorer.tsx +++ b/frontend/src/modules/Usage/UsageExplorer.tsx @@ -13,6 +13,7 @@ import { import { StoreState } from "../../store/reducers"; import moment from "moment"; import { isOnboardingSkipped } from "../../utils/app"; +import { useRoute } from "../RouteProvider"; const { Option } = Select; interface UsageExplorerProps { @@ -56,6 +57,8 @@ const _UsageExplorer = (props: UsageExplorerProps) => { const [selectedInterval, setSelectedInterval] = useState(interval[2]); const [selectedService, setSelectedService] = useState(""); + const { state } = useRoute(); + useEffect(() => { if (selectedTime && selectedInterval) { const maxTime = new Date().getTime() * 1000000; @@ -71,7 +74,9 @@ const _UsageExplorer = (props: UsageExplorerProps) => { }, [selectedTime, selectedInterval, selectedService]); useEffect(() => { - props.getServicesList(props.globalTime); + if (state.USAGE_EXPLORER.isLoaded) { + props.getServicesList(props.globalTime); + } }, []); const data = { From b979c24cb4ca331187ea82a68f1c4461d00cc7cc Mon Sep 17 00:00:00 2001 From: Nidhi Tandon <> Date: Sun, 23 May 2021 15:43:38 +0530 Subject: [PATCH 4/5] refactor: remove unused prop --- frontend/src/modules/Servicemap/ServiceMap.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/modules/Servicemap/ServiceMap.tsx b/frontend/src/modules/Servicemap/ServiceMap.tsx index 8306a35ada..257f505fc3 100644 --- a/frontend/src/modules/Servicemap/ServiceMap.tsx +++ b/frontend/src/modules/Servicemap/ServiceMap.tsx @@ -37,7 +37,6 @@ interface ServiceMapProps extends RouteComponentProps { globalTime: GlobalTime; getServiceMapItems: Function; getDetailedServiceMapItems: Function; - componentPath: string; } interface graphNode { id: string; From fed23a6ab9004ae629663be082e779e110dfe372 Mon Sep 17 00:00:00 2001 From: Nidhi Tandon <> Date: Sun, 23 May 2021 16:06:40 +0530 Subject: [PATCH 5/5] chore: add comments --- frontend/src/modules/Servicemap/ServiceMap.tsx | 4 ++++ frontend/src/modules/Traces/TraceCustomVisualizations.tsx | 4 ++++ frontend/src/modules/Traces/TraceFilter.tsx | 4 ++++ frontend/src/modules/Usage/UsageExplorer.tsx | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/frontend/src/modules/Servicemap/ServiceMap.tsx b/frontend/src/modules/Servicemap/ServiceMap.tsx index 257f505fc3..fe31b8d8fb 100644 --- a/frontend/src/modules/Servicemap/ServiceMap.tsx +++ b/frontend/src/modules/Servicemap/ServiceMap.tsx @@ -64,6 +64,10 @@ const ServiceMap = (props: ServiceMapProps) => { } = props; useEffect(() => { + /* + Call the apis only when the route is loaded. + Check this issue: https://github.com/SigNoz/signoz/issues/110 + */ if (state.SERVICE_MAP.isLoaded) { getServiceMapItems(globalTime); getDetailedServiceMapItems(globalTime); diff --git a/frontend/src/modules/Traces/TraceCustomVisualizations.tsx b/frontend/src/modules/Traces/TraceCustomVisualizations.tsx index 7b26ce4a29..baf3271c64 100644 --- a/frontend/src/modules/Traces/TraceCustomVisualizations.tsx +++ b/frontend/src/modules/Traces/TraceCustomVisualizations.tsx @@ -113,6 +113,10 @@ const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => { maxTime: props.globalTime.maxTime + 15 * 60 * 1000000000, }; + /* + Call the apis only when the route is loaded. + Check this issue: https://github.com/SigNoz/signoz/issues/110 + */ if (state.TRACES.isLoaded) { props.getFilteredTraceMetrics(request_string, plusMinus15); } diff --git a/frontend/src/modules/Traces/TraceFilter.tsx b/frontend/src/modules/Traces/TraceFilter.tsx index 06e69f6fd0..82d679895d 100644 --- a/frontend/src/modules/Traces/TraceFilter.tsx +++ b/frontend/src/modules/Traces/TraceFilter.tsx @@ -124,6 +124,10 @@ const _TraceFilter = (props: TraceFilterProps) => { "&tags=" + encodeURIComponent(JSON.stringify(props.traceFilters.tags)); + /* + Call the apis only when the route is loaded. + Check this issue: https://github.com/SigNoz/signoz/issues/110 + */ if (state.TRACES.isLoaded) { props.fetchTraces(props.globalTime, request_string); } diff --git a/frontend/src/modules/Usage/UsageExplorer.tsx b/frontend/src/modules/Usage/UsageExplorer.tsx index 1e1675e0db..d02cc8eebb 100644 --- a/frontend/src/modules/Usage/UsageExplorer.tsx +++ b/frontend/src/modules/Usage/UsageExplorer.tsx @@ -74,6 +74,10 @@ const _UsageExplorer = (props: UsageExplorerProps) => { }, [selectedTime, selectedInterval, selectedService]); useEffect(() => { + /* + Call the apis only when the route is loaded. + Check this issue: https://github.com/SigNoz/signoz/issues/110 + */ if (state.USAGE_EXPLORER.isLoaded) { props.getServicesList(props.globalTime); }