From 7d378cb2d5e647d8efab6f88fc876baca858d445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Veikko=20Lintuj=C3=A4rvi?= Date: Fri, 7 Aug 2026 19:59:24 +0300 Subject: [PATCH] Improving route handling --- package-lock.json | 58 +++++++++++++++++++++++++++++++++++++ package.json | 1 + src/App.jsx | 20 +++++++++++-- src/components/Hero.jsx | 4 +-- src/components/Navbar.jsx | 21 ++++++-------- src/components/Projects.jsx | 4 +-- src/main.jsx | 5 +++- vite.config.js | 8 +++++ 8 files changed, 102 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0567ace..2556e96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "jotai": "^2.20.0", "react": "^19.2.5", "react-dom": "^19.2.5", + "react-router-dom": "^7.17.0", "sass": "^1.99.0" }, "devDependencies": { @@ -1247,6 +1248,19 @@ "devOptional": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "dev": true, @@ -2209,6 +2223,44 @@ "react": "^19.2.6" } }, + "node_modules/react-router": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz", + "integrity": "sha512-FDELK7rTMlCHO5+reyXsPlmfr7N1F91lPHsWYfMEGQm/KQ+F4JFM8jGoeQDmDvdTs93Fw9aSilH+uKRb4/jXvQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.17.0.tgz", + "integrity": "sha512-fyU2yjGups/hE6Xz0I5ZYbVL8Gx29eCjgpHaRaTaVU+OOAdfRX05KsvyRm0GO8YQwOkhpU3MurW1jyMUJn+zSw==", + "license": "MIT", + "dependencies": { + "react-router": "7.17.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/readdirp": { "version": "4.1.2", "license": "MIT", @@ -2287,6 +2339,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "dev": true, diff --git a/package.json b/package.json index 11ff51f..35ac35d 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "jotai": "^2.20.0", "react": "^19.2.5", "react-dom": "^19.2.5", + "react-router-dom": "^7.17.0", "sass": "^1.99.0" }, "devDependencies": { diff --git a/src/App.jsx b/src/App.jsx index ebb2c77..0569b06 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,6 @@ import { useState, useEffect, useRef } from 'react'; -import { useAtomValue } from 'jotai'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { useAtomValue, useAtom } from 'jotai'; import { activeSectionState, themeState } from './state/atoms'; import Navbar from './components/Navbar'; import Hero from './components/Hero'; @@ -19,11 +20,26 @@ const SECTIONS = { const EXIT_DURATION = 200; export default function App() { - const activeSection = useAtomValue(activeSectionState); + const location = useLocation(); + const navigate = useNavigate(); + const [activeSection, setActiveSection] = useAtom(activeSectionState); const theme = useAtomValue(themeState); const [displayedSection, setDisplayedSection] = useState(activeSection); const timerRef = useRef(null); + // Sync URL path with activeSection + useEffect(() => { + const path = location.pathname.substring(1) || 'home'; + if (Object.keys(SECTIONS).includes(path)) { + if (path !== activeSection) { + setActiveSection(path); + } + } else { + // Invalid path, redirect to home + navigate('/'); + } + }, [location.pathname, activeSection, setActiveSection, navigate]); + useEffect(() => { if (theme === 'light') { document.documentElement.dataset.theme = 'light'; diff --git a/src/components/Hero.jsx b/src/components/Hero.jsx index 98e7617..8f5dc96 100644 --- a/src/components/Hero.jsx +++ b/src/components/Hero.jsx @@ -20,10 +20,10 @@ export default function Hero() {

- + See my work - + Get in touch
diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 97f98f3..418e4c6 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -1,4 +1,5 @@ -import { useAtom } from 'jotai'; +import { Link } from 'react-router-dom'; +import { useAtom, useAtomValue } from 'jotai'; import { navOpenState, activeSectionState } from '../state/atoms'; import ThemeToggle from './ThemeToggle'; import styles from '../styles/components/Navbar.module.scss'; @@ -12,31 +13,27 @@ const NAV_LINKS = [ export default function Navbar() { const [isOpen, setIsOpen] = useAtom(navOpenState); - const [activeSection, setActiveSection] = useAtom(activeSectionState); - - function handleNav(section) { - setActiveSection(section); - setIsOpen(false); - } + const activeSection = useAtomValue(activeSectionState); return (