diff --git a/frontend/src/components/CourseDescriptionPanel/CourseDescriptionPanel.tsx b/frontend/src/components/CourseDescriptionPanel/CourseDescriptionPanel.tsx index 216713271..e7c42c32f 100644 --- a/frontend/src/components/CourseDescriptionPanel/CourseDescriptionPanel.tsx +++ b/frontend/src/components/CourseDescriptionPanel/CourseDescriptionPanel.tsx @@ -5,6 +5,7 @@ import { Typography } from 'antd'; import axios from 'axios'; import { Course, CoursePathFrom, CoursesUnlockedWhenTaken } from 'types/api'; import { CourseTimetable, EnrolmentCapacityData } from 'types/courseCapacity'; +import { CourseDescInfoResCache } from 'types/courseDescription'; import { CourseList } from 'types/courses'; import getEnrolmentCapacity from 'utils/getEnrolmentCapacity'; import prepareUserPayload from 'utils/prepareUserPayload'; @@ -25,12 +26,14 @@ type CourseDescriptionPanelProps = { className?: string; courseCode: string; onCourseClick?: (code: string) => void; + courseDescInfoCache: React.MutableRefObject; }; const CourseDescriptionPanel = ({ className, courseCode, - onCourseClick + onCourseClick, + courseDescInfoCache }: CourseDescriptionPanelProps) => { const { degree, planner } = useSelector((state: RootState) => state); @@ -60,22 +63,39 @@ const CourseDescriptionPanel = ({ useEffect(() => { const getCourseInfo = async () => { try { - const results = await Promise.allSettled([ - axios.get(`/courses/getCourse/${courseCode}`), - axios.get(`/courses/getPathFrom/${courseCode}`), - axios.post( - `/courses/coursesUnlockedWhenTaken/${courseCode}`, - JSON.stringify(prepareUserPayload(degree, planner)) - ), - axios.get(`${TIMETABLE_API_URL}/${courseCode}`) - ]); - - const [courseRes, pathFromRes, unlockedRes, courseCapRes] = results; - - setCourse(unwrap(courseRes)?.data); - setCoursesPathFrom(unwrap(pathFromRes)?.data.courses); - setCoursesUnlocked(unwrap(unlockedRes)?.data); - setCourseCapacity(getEnrolmentCapacity(unwrap(courseCapRes)?.data)); + // if it's not saved already then fetch it + if (!courseDescInfoCache.current[courseCode]) { + const results = await Promise.allSettled([ + axios.get(`/courses/getCourse/${courseCode}`), + axios.get(`/courses/getPathFrom/${courseCode}`), + axios.post( + `/courses/coursesUnlockedWhenTaken/${courseCode}`, + JSON.stringify(prepareUserPayload(degree, planner)) + ), + axios.get(`${TIMETABLE_API_URL}/${courseCode}`) + ]); + + const [courseRes, pathFromRes, unlockedRes, courseCapRes] = results; + + courseDescInfoCache.current[courseCode] = { + course: unwrap(courseRes)?.data, + pathFrom: unwrap(pathFromRes)?.data.courses, + unlocked: unwrap(unlockedRes)?.data, + courseCap: getEnrolmentCapacity(unwrap(courseCapRes)?.data) + }; + } + + const { + course: courseData, + pathFrom, + unlocked, + courseCap + } = courseDescInfoCache.current[courseCode]; + + setCourse(courseData); + setCoursesPathFrom(pathFrom); + setCoursesUnlocked(unlocked); + setCourseCapacity(courseCap); setIsLoading(false); } catch (e) { // eslint-disable-next-line no-console @@ -87,7 +107,7 @@ const CourseDescriptionPanel = ({ // gets the associated info for a course getCourseInfo(); } - }, [courseCode, degree, isLoading, planner]); + }, [courseCode, courseDescInfoCache, degree, isLoading, planner]); if (isLoading || !course) { // either still loading or the course wasn't fetchable (fatal) diff --git a/frontend/src/pages/CourseSelector/CourseSelector.tsx b/frontend/src/pages/CourseSelector/CourseSelector.tsx index d23f00299..d5300db91 100644 --- a/frontend/src/pages/CourseSelector/CourseSelector.tsx +++ b/frontend/src/pages/CourseSelector/CourseSelector.tsx @@ -1,7 +1,8 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import axios from 'axios'; import { Structure } from 'types/api'; +import { CourseDescInfoResCache } from 'types/courseDescription'; import { ProgramStructure } from 'types/structure'; import openNotification from 'utils/openNotification'; import infographic from 'assets/infographicFontIndependent.svg'; @@ -23,6 +24,7 @@ const CourseSelector = () => { const dispatch = useDispatch(); + const courseDescInfoCache = useRef({} as CourseDescInfoResCache); const courseCode = tabs[active]; useEffect(() => { @@ -65,6 +67,7 @@ const CourseSelector = () => { dispatch(addTab(code))} + courseDescInfoCache={courseDescInfoCache} /> ) : ( diff --git a/frontend/src/pages/GraphicalSelector/GraphicalSelector.tsx b/frontend/src/pages/GraphicalSelector/GraphicalSelector.tsx index 1eabe236b..311f2a08b 100644 --- a/frontend/src/pages/GraphicalSelector/GraphicalSelector.tsx +++ b/frontend/src/pages/GraphicalSelector/GraphicalSelector.tsx @@ -1,8 +1,9 @@ -import React, { useState } from 'react'; +import React, { useRef, useState } from 'react'; import { Tabs } from 'antd'; import CourseSearchBar from 'components/CourseSearchBar'; import PageTemplate from 'components/PageTemplate'; import SidebarDrawer from 'components/SidebarDrawer'; +import { CourseDescInfoResCache } from '../../types/courseDescription'; import { COURSE_INFO_TAB, HELP_TAB, PROGRAM_STRUCTURE_TAB } from './constants'; import CourseGraph from './CourseGraph'; import HowToUse from './HowToUse'; @@ -12,6 +13,7 @@ const GraphicalSelector = () => { const [fullscreen, setFullscreen] = useState(false); const [courseCode, setCourseCode] = useState(null); const [activeTab, setActiveTab] = useState(HELP_TAB); + const courseDescInfoCache = useRef({} as CourseDescInfoResCache); const items = [ { @@ -22,6 +24,7 @@ const GraphicalSelector = () => { courseCode={courseCode} key={courseCode} onCourseClick={setCourseCode} + courseDescInfoCache={courseDescInfoCache} /> ) : ( 'No course selected' diff --git a/frontend/src/types/courseDescription.ts b/frontend/src/types/courseDescription.ts new file mode 100644 index 000000000..9210a29dd --- /dev/null +++ b/frontend/src/types/courseDescription.ts @@ -0,0 +1,12 @@ +import { Course, CoursesUnlockedWhenTaken } from './api'; +import { EnrolmentCapacityData } from './courseCapacity'; +import { CourseList } from './courses'; + +type CourseInfo = { + course?: Course; + pathFrom?: CourseList; + unlocked?: CoursesUnlockedWhenTaken; + courseCap?: EnrolmentCapacityData; +}; + +export type CourseDescInfoResCache = Record;