0

I’m building a programming tutorial website using React. Each content page has a sidebar and a main content section with "Previous" and "Next" buttons to navigate topics. However, when I refresh the page while viewing a specific topic (e.g., the 10th), it resets to the first topic instead of staying on the current one.

To resolve this, I’m storing the currentHeadingId and currentSubHeadingId in localStorage, but the issue persists. How can I ensure the page loads the correct topic after a refresh?

Any suggestions or solutions would be appreciated!

export const CourseSection = () => {
    const [isSidebarOpen, setSidebarOpen] = useState(false);

    const toggleSidebar = () => {
        setSidebarOpen((prev) => !prev);
    };


    const params = useParams();
    const { data: AllSubject } = useGetAllSubjectsQuery();
    const { data: allSubheading } = useGetAllSubjectSubHeadingQuery();
    const { data: allTopics } = useGetAllTopicHeadingsQuery();
    const { data: allTopicContent } = useGetAllTopicContentsQuery();
    const { data: allTopicHeadingImage } = useGetTopicImagesQuery();
    const { data: allTopicCodeSnippet } = useGetAllTopicCodeSnippetQuery();
    const { data: allTopicNotes } = useGetAllNotesQuery();

    const [headings, setHeadings] = useState([]);
    const [currentHeadingId, setCurrentHeadingId] = useState(localStorage.getItem("currentHeadingId") || null);
    const [currentSubHeadingId, setCurrentSubHeadingId] = useState(localStorage.getItem("currentSubHeadingId") || null);

    useEffect(() => {
        if (AllSubject && allSubheading && allTopics && AllSubject.data && allSubheading.data && allTopics.data) {
            const processedHeadings = AllSubject && AllSubject.data.filter((sub) => sub.id == params.tid).map((subject) => ({
                ...subject,
                headings: allSubheading.data.filter((heading) => heading.subjectId === subject.id).map((heading) => ({
                    ...heading,
                    subHeadings: allTopics.data.filter((topic) => topic.subHeadingId === heading.id),
                })),
            }));
            setHeadings(processedHeadings);
            if (processedHeadings.length > 0 && processedHeadings[0].headings.length > 0) {
                setCurrentHeadingId(processedHeadings[0].headings[0].id);
                localStorage.setItem("currentHeadingId", processedHeadings[0].headings[0].id)
                setCurrentSubHeadingId(processedHeadings[0].headings[0].subHeadings[0].id);
                localStorage.setItem("currentSubHeadingId", processedHeadings[0].headings[0].subHeadings[0].id)
            } else {
                setCurrentHeadingId(localStorage.getItem("currentHeadingId"))
                setCurrentSubHeadingId(localStorage.getItem("currentSubHeadingId"))
            }
        }
    }, [AllSubject, allSubheading, allTopics]);

    const dispatch = useDispatch();
    const course_selector = useSelector(state => state.course.all_contents);
    useEffect(() => {
        dispatch(get_all_contents({ topic_id: params.cid, AllSubject, allTopics, allTopicContent, allTopicHeadingImage, allTopicCodeSnippet, allTopicNotes }))
    }, [params, AllSubject, allTopics, allTopicContent, allTopicHeadingImage, allTopicCodeSnippet, allTopicNotes]);

    const getContentForCurrentSubHeading = () => {
        const currentAnswer = allTopicContent && allTopicContent.data?.find((ans) => ans.topicHeadingId === currentSubHeadingId);
        const currentPhoto = allTopicHeadingImage && allTopicHeadingImage.data?.find((img) => img.topicHeadingId === currentSubHeadingId);
        const currentCodeSnippet = allTopicCodeSnippet && allTopicCodeSnippet.data?.find((cs) => cs.topicHeadingId === currentSubHeadingId);
        const currentNotes = allTopicNotes && allTopicNotes.data?.find((note) => note.topicHeadingId === currentSubHeadingId);
        const currentSubHeading = allTopics && allTopics.data?.find((sub) => sub.id === currentSubHeadingId);

        const content = course_selector && course_selector.filter((content) => content.topicHeadingId === currentSubHeadingId)
        return {
            title: currentSubHeading ? currentSubHeading.topicHeadingName : '',
            contents: content
        };
    };

    const selectSubHeading = (headingId, subHeadingId) => {
        setCurrentHeadingId(headingId);
        setCurrentSubHeadingId(subHeadingId);
    };

    const handlePrev = () => {
        const currentHeading = headings.flatMap(sub => sub.headings).find((h) => h.id === currentHeadingId);
        const subHeadings = currentHeading ? currentHeading.subHeadings : [];
        const currentIndex = subHeadings.findIndex((sub) => sub.id === currentSubHeadingId);

        if (currentIndex > 0) {
            setCurrentSubHeadingId(subHeadings[currentIndex - 1].id);
        } else {
            const prevHeadingIndex = headings.flatMap(sub => sub.headings).findIndex((h) => h.id === currentHeadingId) - 1;
            if (prevHeadingIndex >= 0) {
                const prevHeading = headings.flatMap(sub => sub.headings)[prevHeadingIndex];
                setCurrentHeadingId(prevHeading.id);
                setCurrentSubHeadingId(prevHeading.subHeadings[prevHeading.subHeadings.length - 1].id);
            }
        }
    };

    const handleNext = () => {
        const currentHeading = headings.flatMap(sub => sub.headings).find((h) => h.id === currentHeadingId);
        const subHeadings = currentHeading ? currentHeading.subHeadings : [];
        const currentIndex = subHeadings.findIndex((sub) => sub.id === currentSubHeadingId);

        if (currentIndex < subHeadings.length - 1) {
            setCurrentSubHeadingId(subHeadings[currentIndex + 1].id);
        } else {
            const nextHeadingIndex = headings.flatMap(sub => sub.headings).findIndex((h) => h.id === currentHeadingId) + 1;
            if (nextHeadingIndex < headings.flatMap(sub => sub.headings).length) {
                const nextHeading = headings.flatMap(sub => sub.headings)[nextHeadingIndex];
                setCurrentHeadingId(nextHeading.id);
                setCurrentSubHeadingId(nextHeading.subHeadings[0].id);
            }
        }
    };

    const content = getContentForCurrentSubHeading();
    // console.log(content)

    return (
        <div className="flex h-screen">
            <Sidebar headings={headings && headings.length > 0 && headings} selectSubHeading={selectSubHeading} />
            <MainContent content={content} handlePrev={handlePrev} handleNext={handleNext} />
        </div>
    );

};

I tried storing the **currentHeadingId **and **currentSubHeadingId **in **localStorage **when navigating between topics. On page load, I expected to retrieve these values from localStorage and set the correct topic as active. However, despite implementing this approach, the page still resets to the first topic upon refresh instead of loading the intended topic.

1 Answer 1

0

Instead of localstorage, you can store current page in URL query params, which is common and best practice to handle those kind of caching. Base on URL query params, you can easily retreive values like currentHeadingId or currentSubHeadingId. https://v5.reactrouter.com/web/example/query-parameters

How to get parameter value from query string?

Probably on button click (next/previous) you will have to call something like that to set query params:

import { useSearchParams } from 'react-router-dom';

function Component() {
  const [searchParams, setSearchParams] = useSearchParams();
  const [currentHeadingId, setCurrentHeadingId] = useState(searchParams.get("currentHeadingId"));
  const [currentSubHeadingId, setCurrentSubHeadingId] = useState(searchParams.get("currentSubHeadingId")) || null);
  ...
  const handleNext = () => {
    setSearchParams('currentHeadingId', currentHeadingId);
    setSearchParams('currentSubHeadingId', currentSubHeadingId);
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

I am using RouterV6
In second article that i linked in my response, you have explanation, how you can use query params via react-router-v6. This article may be helpful as well robinwieruch.de/react-router-search-params

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.