ref(onboarding): fix cache when platforms are the same
diff --git a/static/app/components/onboardingWizard/useOnboardingDocs.tsx b/static/app/components/onboardingWizard/useOnboardingDocs.tsx
index 99d1c97c4550d..29ddcb4e1a9b4 100644
--- a/static/app/components/onboardingWizard/useOnboardingDocs.tsx
+++ b/static/app/components/onboardingWizard/useOnboardingDocs.tsx
@@ -1,4 +1,4 @@
-import {useEffect, useState} from 'react';
+import {useEffect, useRef, useState} from 'react';
 import * as Sentry from '@sentry/react';
 
 import {loadDocs} from 'sentry/actionCreators/projects';
@@ -17,44 +17,44 @@ type Options = {
 };
 
 function useOnboardingDocs({docKeys, isPlatformSupported, project}: Options) {
-  const organization = useOrganization();
   const api = useApi();
+  const organization = useOrganization();
+
+  const loadingDocsRef = useRef<Record<string, boolean>>(INITIAL_LOADING_DOCS);
+  const docContentsRef = useRef<Record<string, string>>(INITIAL_DOC_CONTENTS);
 
-  const [loadingDocs, setLoadingDocs] =
-    useState<Record<string, boolean>>(INITIAL_LOADING_DOCS);
   const [docContents, setDocContents] =
     useState<Record<string, string>>(INITIAL_DOC_CONTENTS);
 
-  const currentPlatform = project.platform
-    ? platforms.find(p => p.id === project.platform)
-    : undefined;
+  docContentsRef.current = docContents;
 
   useEffect(() => {
     if (!isPlatformSupported) {
-      if (loadingDocs !== INITIAL_LOADING_DOCS) {
-        setLoadingDocs(INITIAL_LOADING_DOCS);
+      if (loadingDocsRef.current !== INITIAL_LOADING_DOCS) {
+        loadingDocsRef.current = INITIAL_LOADING_DOCS;
       }
-      if (docContents !== INITIAL_DOC_CONTENTS) {
+      if (docContentsRef.current !== INITIAL_DOC_CONTENTS) {
         setDocContents(INITIAL_DOC_CONTENTS);
       }
-      return;
+      return undefined;
     }
 
+    let cancelRequest = false;
+
     docKeys.forEach(docKey => {
-      if (docKey in loadingDocs) {
+      if (docKey in loadingDocsRef.current) {
         // If a documentation content is loading, we should not attempt to fetch it again.
         // otherwise, if it's not loading, we should only fetch at most once.
         // Any errors that occurred will be captured via Sentry.
         return;
       }
 
-      const setLoadingDoc = (loadingState: boolean) =>
-        setLoadingDocs(prevState => {
-          return {
-            ...prevState,
-            [docKey]: loadingState,
-          };
-        });
+      const setLoadingDoc = (loadingState: boolean) => {
+        loadingDocsRef.current = {
+          ...loadingDocsRef.current,
+          [docKey]: loadingState,
+        };
+      };
 
       const setDocContent = (docContent: string | undefined) =>
         setDocContents(prevState => {
@@ -80,25 +80,33 @@ function useOnboardingDocs({docKeys, isPlatformSupported, project}: Options) {
         platform: docKey as any,
       })
         .then(({html}) => {
-          setDocContent(html as string);
+          if (cancelRequest) {
+            return;
+          }
           setLoadingDoc(false);
+          setDocContent(html as string);
         })
         .catch(error => {
+          if (cancelRequest) {
+            return;
+          }
           Sentry.captureException(error);
-          setDocContent(undefined);
           setLoadingDoc(false);
+          setDocContent(undefined);
         });
     });
-  }, [
-    currentPlatform,
-    docKeys,
-    isPlatformSupported,
-    api,
-    loadingDocs,
-    organization.slug,
-    project.slug,
-    docContents,
-  ]);
+
+    return () => {
+      cancelRequest = true;
+      for (const key of docKeys) {
+        delete loadingDocsRef.current[key];
+      }
+    };
+  }, [docKeys, isPlatformSupported, api, organization.slug, project]);
+
+  const currentPlatform = project.platform
+    ? platforms.find(p => p.id === project.platform)
+    : undefined;
 
   if (!currentPlatform || !isPlatformSupported) {
     return {
@@ -108,23 +116,20 @@ function useOnboardingDocs({docKeys, isPlatformSupported, project}: Options) {
     };
   }
 
-  const isLoading = Boolean(
-    docKeys?.some(key => {
-      if (key in loadingDocs) {
-        return !!loadingDocs[key];
+  const isLoading =
+    docKeys &&
+    docKeys.some(key => {
+      if (key in loadingDocsRef.current) {
+        return !!loadingDocsRef.current[key];
       }
       return true;
-    })
-  );
-
-  const hasOnboardingContents = Boolean(
-    docKeys?.every(key => typeof docContents[key] === 'string')
-  );
+    });
 
   return {
     docKeys,
     isLoading,
-    hasOnboardingContents,
+    hasOnboardingContents:
+      docKeys && docKeys.every(key => typeof docContents[key] === 'string'),
     docContents,
   };
 }
diff --git a/static/app/components/performanceOnboarding/sidebar.tsx b/static/app/components/performanceOnboarding/sidebar.tsx
index 8097cb0815feb..50451c1dd1f50 100644
--- a/static/app/components/performanceOnboarding/sidebar.tsx
+++ b/static/app/components/performanceOnboarding/sidebar.tsx
@@ -1,4 +1,4 @@
-import {Fragment, useEffect, useState} from 'react';
+import {Fragment, useEffect, useMemo, useState} from 'react';
 import styled from '@emotion/styled';
 
 import HighlightTopRightPattern from 'sentry-images/pattern/highlight-top-right.svg';
@@ -182,7 +182,10 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
     ? platforms.find(p => p.id === currentProject.platform)
     : undefined;
 
-  const docKeys = currentPlatform ? generateDocKeys(currentPlatform.id) : [];
+  const docKeys = useMemo(() => {
+    return currentPlatform ? generateDocKeys(currentPlatform.id) : [];
+  }, [currentPlatform]);
+
   const {docContents, isLoading, hasOnboardingContents} = useOnboardingDocs({
     project: currentProject,
     docKeys,
diff --git a/static/app/components/profiling/ProfilingOnboarding/profilingOnboardingSidebar.tsx b/static/app/components/profiling/ProfilingOnboarding/profilingOnboardingSidebar.tsx
index 2462f812209ba..ca703384591f3 100644
--- a/static/app/components/profiling/ProfilingOnboarding/profilingOnboardingSidebar.tsx
+++ b/static/app/components/profiling/ProfilingOnboarding/profilingOnboardingSidebar.tsx
@@ -214,9 +214,13 @@ function OnboardingContent({
   }, [currentProject, previousProject]);
 
   const docKeysMap = useMemo(() => makeDocKeyMap(currentPlatform?.id), [currentPlatform]);
+  const docKeys = useMemo(
+    () => (docKeysMap ? Object.values(docKeysMap) : []),
+    [docKeysMap]
+  );
 
   const {docContents, isLoading, hasOnboardingContents} = useOnboardingDocs({
-    docKeys: docKeysMap ? Object.values(docKeysMap) : [],
+    docKeys,
     project: currentProject,
     isPlatformSupported: isSupported,
   });
diff --git a/static/app/components/replaysOnboarding/sidebar.tsx b/static/app/components/replaysOnboarding/sidebar.tsx
index fecf2af34b024..be70a2c9c4313 100644
--- a/static/app/components/replaysOnboarding/sidebar.tsx
+++ b/static/app/components/replaysOnboarding/sidebar.tsx
@@ -154,7 +154,9 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
     ? platforms.find(p => p.id === currentProject.platform)
     : undefined;
 
-  const docKeys = currentPlatform ? generateDocKeys(currentPlatform.id) : [];
+  const docKeys = useMemo(() => {
+    return currentPlatform ? generateDocKeys(currentPlatform.id) : [];
+  }, [currentPlatform]);
 
   const {docContents, isLoading, hasOnboardingContents} = useOnboardingDocs({
     project: currentProject,

The documentation needs to be re-fetched when the project changes so that they dsn in the response is updated.

Fixes https://github.com/getsentry/sentry/issues/59965

