diff --git a/frontend/src/modules/AppWrapper.tsx b/frontend/src/modules/AppWrapper.tsx index b261ad3e32..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,36 +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..6e3f52769e 100644 --- a/frontend/src/modules/BaseLayout.tsx +++ b/frontend/src/modules/BaseLayout.tsx @@ -1,8 +1,12 @@ -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 { @@ -10,6 +14,13 @@ interface BaseLayoutProps { } const BaseLayout: React.FC = ({ children }) => { + const location = useLocation(); + const { dispatch } = useRoute(); + + useEffect(() => { + dispatch({ type: "UPDATE_IS_LOADED", payload: location.pathname }); + }, [location]); + return ( diff --git a/frontend/src/modules/RouteProvider.tsx b/frontend/src/modules/RouteProvider.tsx new file mode 100644 index 0000000000..0680e912c8 --- /dev/null +++ b/frontend/src/modules/RouteProvider.tsx @@ -0,0 +1,83 @@ +import React, { useContext, createContext, ReactNode, Dispatch } from "react"; +import ROUTES from "Src/constants/routes"; + +type State = { + [key: string]: { + route: string; + isLoaded: boolean; + }; +}; + +enum ActionTypes { + UPDATE_IS_LOADED = "UPDATE_IS_LOADED", +} + +type Action = { + type: ActionTypes; + payload: string; +}; + +interface ContextType { + state: State; + dispatch: Dispatch; +} + +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 === 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, + }; + } + return { + ...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, getInitialState()); + 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 048c6980b9..fe31b8d8fb 100644 --- a/frontend/src/modules/Servicemap/ServiceMap.tsx +++ b/frontend/src/modules/Servicemap/ServiceMap.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useContext, useEffect, useRef } from "react"; import { connect } from "react-redux"; import { RouteComponentProps } from "react-router-dom"; import { @@ -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 { @@ -53,6 +54,8 @@ export interface graphDataType { const ServiceMap = (props: ServiceMapProps) => { const fgRef = useRef(); + const { state } = useRoute(); + const { getDetailedServiceMapItems, getServiceMapItems, @@ -61,8 +64,14 @@ const ServiceMap = (props: ServiceMapProps) => { } = props; useEffect(() => { - getServiceMapItems(globalTime); - getDetailedServiceMapItems(globalTime); + /* + 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); + } }, [globalTime]); useEffect(() => { diff --git a/frontend/src/modules/Traces/TraceCustomVisualizations.tsx b/frontend/src/modules/Traces/TraceCustomVisualizations.tsx index 3c81ad244b..baf3271c64 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,14 @@ const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => { minTime: props.globalTime.minTime - 15 * 60 * 1000000000, maxTime: props.globalTime.maxTime + 15 * 60 * 1000000000, }; - props.getFilteredTraceMetrics(request_string, plusMinus15); + + /* + 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); + } }, [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..82d679895d 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,13 @@ const _TraceFilter = (props: TraceFilterProps) => { "&tags=" + encodeURIComponent(JSON.stringify(props.traceFilters.tags)); - props.fetchTraces(props.globalTime, request_string); + /* + 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); + } }, [props.traceFilters, props.globalTime]); useEffect(() => { diff --git a/frontend/src/modules/Usage/UsageExplorer.tsx b/frontend/src/modules/Usage/UsageExplorer.tsx index 3152e12693..d02cc8eebb 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,13 @@ const _UsageExplorer = (props: UsageExplorerProps) => { }, [selectedTime, selectedInterval, selectedService]); useEffect(() => { - props.getServicesList(props.globalTime); + /* + 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); + } }, []); const data = {