Single Page Applications (SPA) update elements within a web app or site, instead of refreshing the whole page.
Major advantages of SPA include smooth interaction akin to a native app and less network traffic.
They also come with non-trivial downsides: slow loading time (since it downloads all static resources all at once), and Search Engine Optimization (SEO) issues (Googlebot easily indexes the page using HTML/CSS data, and has to deploy a more advanced method for indexing SPAs. Also search engines rank not websites but website pages, but for SPAs all the pages are combined into one which minimizes its chances to get ranked on different keywords.) Alot of resources cover various strategies of overcoming this SEO problem specifically concerning SPA.
Using React's React Router
library, we can update the view inside an HTML container by associating specific paths with specific elements/components, and then linking those paths with specific triggers (i.e. navigation icons, text.) You can connect the nav elements with the paths via Link
, and specify the Routes
inside the HTML element you want to update (the individual Route
elements should be grouped inside Routes
, then the main div/App component should be wrapped by BrowserRouter
.)
First we need to install the react-router library: npm install react-router-dom@^6.3.0
Import the router, and wrap the entire root component with the BrowserRouter
in the index.js
file (best practice)
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter} from 'react-router-dom'; //like this
import 'bootstrap/dist/css/bootstrap.css';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'));
Routes
"*"
<Route path="*" element={<NotFound />}></Route>
import {React, useState} from 'react';
import './App.css';
import './global-style.css';
import { Routes, Route} from 'react-router-dom';
import Sidebar from './Sidebar';
import Tweets from './Pages/Tweets';
import MyPage from './Pages/MyPage';
import About from './Pages/About';
const App = () => {
return (
<div>
<div className="App">
<main>
<Sidebar />
<section className="features">
<Routes>
<Route path="/" element={<Tweets />} />
<Route path="/about" element={<About />} />
<Route path="/mypage" element={<MyPage />} />
</Routes>
</section>
</main>
</div>
</div>
);
};
export default App;
Link
Link the navigational elements with the Routes
import React from 'react';
import {Link} from 'react-router-dom';
const Sidebar = () => {
return (
<section className="sidebar">
<Link to ="/"><i className="far fa-comment-dots"></i></Link>
<Link to ="/about"><i className="far fa-question-circle"></i></Link>
<Link to="/mypage"><i className="far fa-user"></i></Link>
</section>
);
};
export default Sidebar;
useNavigate
const navigate = useNavigate()
<Link to>
) you want to navigate to, OR{replace, state}
false
is the default. replace: true
means you can't go back (clicking the back arrow in the browser will go back to the main page ("/"))state
is where you can pass values while moving between pagesnavigate('/route', {state: {key: value, key: value ....}})
useLocation()
const Sidebar = () => {
const navigate = useNavigate();
const goBack = () => navigate(-1);
const goForward = () => navigate(1);
return (
<section className="sidebar"> //you can also write, button onclick = {() => {navigate("/about")}}
<button onClick={goBack} className="navigate"><i className="far fa-circle-left"></i></button>
<button onClick={goForward} className="navigate"><i className="far fa-circle-right"></i></button>
<Link to ="/"><i className="far fa-comment-dots"></i></Link>
<Link to ="/about"><i className="far fa-question-circle"></i></Link>
<Link to="/mypage"><i className="far fa-user"></i></Link>
</section>
);
};
For instance, navigating to different pages depending on whether the user is logged in or not
import { useNavigate } from "react-router-dom";
function MypageForm() {
let navigate = useNavigate();
function handleClick() {
if(loggedin) navigate('/mypage');
else navigate('/login');
}
return <button onClick={handleClick}>myPage</button>;
}
React.useEffect
hook to add the event listener only after the component finishes rendering useEffect
callback, it runs when the component unmounts, which can be used to remove teh event listener when the component is no longer mounted! const App = (props) => {
const handleKeyDown = (event) => {
console.log('A key was pressed', event.keyCode);
};
React.useEffect(() => {
window.addEventListener('keydown', handleKeyDown);
// cleanup this component
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, []);
return (
<div className='container'>
<h1>Welcome to the Keydown Listening Component</h1>
</div>
);
};
const App = () => {
React.useEffect(() => {
window.addEventListener('resize', (e) => {
// const sidebar =
if (window.matchMedia("(min-width: 500px)").matches) {
console.log("Screen width is at least 500px");
} else {
console.log("Screen less than 500px");
}
})
}, []);
return (
<div className="App">
<main>
<Sidebar />
<Features />
</main>
</div>
);
};
Application in Sidebar (combining useNavigate
& EventListener
)
const Sidebar = () => {
const navigate = useNavigate();
const goBack = () => navigate(-1);
const goForward = () => navigate(1);
const handleKeyDown = (e) => {
if (e.key === "ArrowLeft") goBack();
else if (e.key === "ArrowRight") goForward();
}
React.useEffect(() => {
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
}
})
return (
<section className="sidebar">
<button onClick={goBack} className="navigate"><i className="far fa-circle-left"></i></button>
<button onClick={goForward} className="navigate"><i className="far fa-circle-right"></i></button>
<Link to ="/"><i className="far fa-comment-dots"></i></Link>
<Link to ="/about"><i className="far fa-question-circle"></i></Link>
<Link to="/mypage"><i className="far fa-user"></i></Link>
</section>
);
};
export default Sidebar;