Overview
The SSEGH Security Cameras application uses React Router v6 with HashRouter for client-side routing. HashRouter enables static deployment without server-side configuration by using the URL hash portion for navigation.
Router Configuration
HashRouter Setup
The application uses HashRouter instead of BrowserRouter to support static hosting:
< HashRouter basename = "/" >
< NavbarTop />
< Routes >
< Route path = "/" element = { < Principal /> } ></ Route >
< Route path = "/productos" element = { < Main /> } ></ Route >
< Route path = "/nosotros" element = { < Nosotros /> } ></ Route >
< Route path = "/contacto" element = { < Contacto /> } ></ Route >
< Route path = "*" element = { < NotFound /> } ></ Route >
< Route path = "/productos/camaras/:modelo" element = { < ViewCamera /> } ></ Route >
< Route path = "/productos/camaras/filtro/:filter" element = { < FilterCamara /> } ></ Route >
</ Routes >
</ HashRouter >
HashRouter uses # in URLs (e.g., example.com/#/productos) which works on any static hosting platform without additional server configuration.
Route Aliases
The application imports Router components through centralized aliases in utilities/const.js:
const Routes = ReactRouterDOM . Routes
const Route = ReactRouterDOM . Route
const BrowserRouter = ReactRouterDOM . BrowserRouter
const Link = ReactRouterDOM . Link
const Navigate = ReactRouterDOM . Navigate
const HashRouter = ReactRouterDOM . HashRouter
const useLocation = ReactRouterDOM . useLocation
const useNavigate = ReactRouterDOM . useNavigate
const useParams = ReactRouterDOM . useParams
This approach provides consistent imports across all components without module bundlers.
Route Definitions
Static Routes
The application defines five static routes for main pages:
< Route path = "/" element = { < Principal /> } ></ Route >
Renders the home page with hero content: components/principal.js:2-10
return (
< div className = "col position-absolute" id = "portada" >
< div className = "position-absolute" id = "info" >
< p > Seguridad </ p >
< p > para tu </ p >
< p > negocio y hogar </ p >
</ div >
</ div >
)
/productos - Product Catalog
< Route path = "/productos" element = { < Main /> } ></ Route >
Displays the full product catalog with sidebar filters. The Main component resets state on mount: useEffect (() => {
dispatch ( backToInitialState ( 'reset' ))
}, [ dispatch ])
< Route path = "/nosotros" element = { < Nosotros /> } ></ Route >
Renders company information and team details.
< Route path = "/contacto" element = { < Contacto /> } ></ Route >
Displays contact form and business information.
< Route path = "*" element = { < NotFound /> } ></ Route >
Catches all undefined routes and displays a 404 page.
Dynamic Routes
The application includes two dynamic routes with URL parameters:
Camera Detail Route
< Route path = "/productos/camaras/:modelo" element = { < ViewCamera /> } ></ Route >
This route displays individual camera details. The ViewCamera component extracts the modelo parameter:
components/viewcamera.js:42-55
const ViewCamera = () => {
const { useDispatch , useSelector } = ReactRedux
const { useParams } = ReactRouterDOM
const { useEffect } = React
const dispatch = useDispatch ()
const [ modelo ] = Object . values ( useParams ())
const [ camara ] = useSelector (( state ) => state . camaras )
useEffect ( () => {
dispatch ( filterModelo ( modelo ))
}, [ dispatch ])
return ( camara . length > 1 ? < p > loading... </ p > : < CardCamara camara = { camara } /> )
}
Extract Parameter
Use useParams() to access the modelo value from the URL.
Filter State
Dispatch filterModelo action to filter Redux state to the matching camera.
Render Details
Display the camera details using the CardCamara component.
Filter Route
< Route path = "/productos/camaras/filtro/:filter" element = { < FilterCamara /> } ></ Route >
This route displays filtered camera results based on user-selected criteria. The :filter parameter contains URL-encoded filter values.
Navigation
Link Component
The application uses React Router’s Link component for declarative navigation:
components/navbarTop.js:3-11
const listItems = options . map (( option , i , array ) => {
const url = "/" + option . toLowerCase ()
return (
< li key = { option } className = "nav-item" >
{ option . toLowerCase () === "productos" ?
< Link className = "links-barra" to = { url } > { option } </ Link > :
< Link className = "links-barra" to = { url } > { option } </ Link > }
</ li >
)
})
The Link component prevents full page reloads, providing a smooth single-page application experience.
Programmatic Navigation
Components use the useNavigate hook for programmatic navigation:
components/navbarAside.js:18-22
const navigate = useNavigate ()
< button className = "btn btn-link"
onClick = { () => {
dispatch ( filterCamarasAside ([ key ]))
let url = urlFiltro ( key , location )
navigate ( `/productos/camaras/filtro/ ${ url } ` )} }
> { key } </ button >
Navigation with State
You can pass state during navigation:
components/navbarAside.js:55
navigate ( '/productos' , { state: { text: [ 'reset' ]}})
Access this state in the destination component using useLocation:
const location = useLocation ()
const stateData = location . state
Back Navigation
The application implements back navigation for returning to previous pages:
components/viewcamera.js:32-34
< button type = "button" onClick = { () => navigate ( - 1 ) } className = "btn btn-volver" >
Volver al listado
</ button >
navigate(-1) works like the browser’s back button, preserving the navigation history.
Navigation Patterns
Filter Navigation Flow
The application implements a sophisticated filter navigation pattern:
Initial State
User views all products at /productos.
Apply Filter
Clicking a filter button dispatches Redux action and navigates to filter URL: components/navbarAside.js:19-22
onClick = {()=> {
dispatch ( filterCamarasAside ([ key ]))
let url = urlFiltro ( key , location )
navigate ( `/productos/camaras/filtro/ ${ url } ` )}}
Display Results
The FilterCamara component renders filtered products.
Add More Filters
Additional filters update the URL and Redux state without page reload.
Remove Filters
Clicking the X on a filter badge removes it and updates the route: components/navbarAside.js:53-59
onClick = {()=> {
let resultado = createSlug ( createUrlFilter ( filter , filtro ))
if ( ! resultado ){ navigate ( '/productos' , { state: { text: [ 'reset' ]}})}
else {
navigate ( `/productos/camaras/filtro/ ${ resultado } ` , { replace: true })
dispatch ( backToInitialState ([ filtro ]))
}
}}
The replace: true option replaces the current history entry instead of adding a new one, preventing unwanted back button behavior.
Route Parameters
Accessing Parameters
Use the useParams hook to access URL parameters:
components/viewcamera.js:44-47
const { useParams } = ReactRouterDOM
const { modelo } = useParams ()
// or
const [ modelo ] = Object . values ( useParams ())
Using Parameters with Redux
Route parameters typically trigger Redux actions to filter or load data:
components/viewcamera.js:50-52
useEffect ( () => {
dispatch ( filterModelo ( modelo ))
}, [ dispatch ])
This pattern ensures the component displays the correct data based on the URL.
Location Awareness
The useLocation hook provides access to the current location object:
components/navbarAside.js:6
const location = useLocation ()
Use this to:
Build dynamic URLs based on current location
Implement breadcrumbs
Conditionally render UI based on current route
Access navigation state
The application uses the Navigation API to handle footer styling based on routes:
const urls = [ "productos" , "nosotros" , "contacto" ]
const footer = document . getElementById ( "footer" )
window . navigation . addEventListener ( "navigate" , ( event ) => {
if ( ! ( urls . find ( s => event . destination . url . includes ( s )) !== undefined )){
footer . classList . add ( "overloap" )
}
else { if ( footer . classList . length > 1 ) footer . classList . remove ( "overloap" )}
})
window . addEventListener ( "DOMContentLoaded" , ( event ) => {
if ( ! urls . some ( s => window . location . href . includes ( s ))) footer . classList . add ( "overloap" )
})
This adds the overloap class to the footer on the landing page for visual effect.
Best Practices
Use Link for Navigation Always use <Link> instead of <a> tags for internal navigation to maintain SPA behavior.
Navigate Programmatically Use useNavigate() for navigation triggered by user actions like button clicks or form submissions.
Reset State on Route Change Dispatch actions in useEffect to ensure components display correct data when routes change.
Handle URL Parameters Extract and validate URL parameters before using them to dispatch actions or render content.
Next Steps
Architecture Explore the overall application structure and patterns
State Management Learn how Redux manages application state