Skip to main content

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:
app.js:16-27
<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:
utilities/const.js:4-12
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>
)
<Route path="/productos" element={<Main />}></Route>
Displays the full product catalog with sidebar filters. The Main component resets state on mount:
components/main.js:6-8
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

app.js:24
<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}/>)
}
1

Extract Parameter

Use useParams() to access the modelo value from the URL.
2

Filter State

Dispatch filterModelo action to filter Redux state to the matching camera.
3

Render Details

Display the camera details using the CardCamara component.

Filter Route

app.js:25
<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. 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>
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.

Filter Navigation Flow

The application implements a sophisticated filter navigation pattern:
1

Initial State

User views all products at /productos.
2

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}`)}}
3

Display Results

The FilterCamara component renders filtered products.
4

Add More Filters

Additional filters update the URL and Redux state without page reload.
5

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:
index.html:82-93
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

Build docs developers (and LLMs) love