React Hooks - Invalid Hook Call - reactjs

I am getting an error
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
This is my hook useParking:
import { Strings } from "..";
import { get } from "../HTTPProvider";
import { Lot } from ".";
import { Moment } from "moment";
import L from "leaflet";
interface ApiResponse {
id: string;
fields: Lot;
createdTime: Moment | string;
}
const { apiUrl, apiKey } = Strings;
const [error, setError] = useState(false);
const getParkingLots = (setParkingLots: (l: Lot[]) => void) => {
get<{ records: ApiResponse[] }>(`${apiUrl}Parking%20Lots`, { apiKey })
.then((response) => {
console.log(response);
const data: Lot[] = [];
response.data.records.forEach((record) => {
const lat = record.fields.latitude;
const lon = record.fields.longitude;
if (lat && lon) record.fields.coordinates = L.latLng([lat, lon]);
data.push(record.fields);
});
setParkingLots(data);
})
.catch((error) => setError(error));
console.log(error);
};
export const useParkingLot = (): Lot[] => {
const [parkingLots, setParkingLots] = useState<Lot[]>([]);
useEffect(() => {
getParkingLots(setParkingLots);
}, [parkingLots]);
return parkingLots;
};
I am trying to use the hook in my MainTabs component here:
import {
IonTabs,
IonTabBar,
IonTabButton,
IonIcon,
IonLabel,
IonRouterOutlet,
} from "#ionic/react";
import { map, business, calendarOutline, carOutline } from "ionicons/icons";
import { Route, Redirect } from "react-router";
import { CampusMap, Events, Buildings, ParkingLots } from "../../pages";
import { useFakeEvent } from "../../DataProviders";
import { useBuilding } from "../../DataProviders";
import { useParkingLot } from "../../DataProviders";
export const MainTabs: React.FC = () => {
const buildings = useBuilding();
const parkingLots = useParkingLot();
const [events, setEvents] = useState(useFakeEvent());
const [showName, setShowName] = useState(true);
const toggleName = () => {
console.log("resetName called");
setShowName(false);
return setTimeout(() => {
setShowName(true);
});
};
return (
<IonTabs>
<IonRouterOutlet>
<Route path="/:tab(Events)" render={() => <Events />} exact={true} />
<Route
path="/:tab(Map)"
render={() => (
<CampusMap
buildings={buildings}
showName={showName}
parkingLots={parkingLots}
events={events}
/>
)}
exact={true}
/>
<Route
path="/:tab(BuildingList)"
render={() => <Buildings buildings={buildings} />}
exact={true}
/>
<Route
path="/:tab(ParkingLotList)"
render={() => <ParkingLots parkingLots={parkingLots} />}
exact={true}
/>
<Route exact path="/" render={() => <Redirect to="/Map" />} />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="Map" href="/Map" onClick={toggleName}>
<IonIcon icon={map} />
<IonLabel>Map</IonLabel>
</IonTabButton>
<IonTabButton tab="BuildingList" href="/BuildingList">
<IonIcon icon={business} />
<IonLabel>Buildings</IonLabel>
</IonTabButton>
<IonTabButton tab="Events" href="/Events">
<IonIcon icon={calendarOutline} />
<IonLabel>Events</IonLabel>
</IonTabButton>
<IonTabButton tab="ParkingList" href="/ParkingLotList">
<IonIcon icon={carOutline} />
<IonLabel>Parking Lots</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
};
I have checked my code against the Rules of Hook documentation and it doesn't seem like I am breaking any. I have also checked my dependencies and they all check out. So I'm not sure why I'm getting the error. Can anyone see what I am doing wrong here?

Use useState inside getParkingLots function because you defined useState outside of functional component which causes Invalid Hook Call. Try initializing inside like
const getParkingLots = (setParkingLots: (l: Lot[]) => void) => {
const [error, setError] = useState(false); // here
get<{ records: ApiResponse[] }>(`${apiUrl}Parking%20Lots`, { apiKey })
.then((response) => {
....
})
.catch((error) => setError(error));
console.log(error);
};

The const [error, setError] = useState(false); needs to be within a functional component body or custom hook. – Drew Reese
This fixed the error.

Related

React Router Dom - v6 - useBlocker

As https://github.com/remix-run/react-router/issues/8139 is finished and we got useBlocker in v6, did anyone got it to work?
This is what I got so far and pretty much I'm stuck with error I quite don't understand
in App.js I have my BrowserRouter and everything is wrapped inside
Also I used example from implementer's gists (I copy pasted)
import * as React from "react";
import { useBeforeUnload, unstable_useBlocker as useBlocker } from "react-router-dom";
function usePrompt(message, { beforeUnload } = {}) {
let blocker = useBlocker(
React.useCallback(
() => (typeof message === "string" ? !window.confirm(message) : false),
[message]
)
);
let prevState = React.useRef(blocker.state);
React.useEffect(() => {
if (blocker.state === "blocked") {
blocker.reset();
}
prevState.current = blocker.state;
}, [blocker]);
useBeforeUnload(
React.useCallback(
(event) => {
if (beforeUnload && typeof message === "string") {
event.preventDefault();
event.returnValue = message;
}
},
[message, beforeUnload]
),
{ capture: true }
);
}
function Prompt({ when, message, ...props }) {
usePrompt(when ? message : false, props);
return null;
}
And then within my component I called Prompt like this
const MyComponent = (props) => {
const [showPrompt, setShowPrompt] = useState(false)
...
return (
...
<Prompt when={showPrompt}
message="Unsaved changes detected, continue?"
beforeUnload={true}
/>
)
}
And on page load of MyComponent I keep getting error
Error: useBlocker must be used within a data router. See
https://reactrouter.com/routers/picking-a-router.
at invariant (history.ts:308:1)
at useDataRouterContext (hooks.tsx:523:1)
at useBlocker (hooks.tsx:723:1)
at usePrompt (routerCustomPrompt.js:8:1)
at Prompt (routerCustomPrompt.js:37:1)
Did anyone got useBlocker in new version to work?
The error message is rather clear. In order to use the useBlocker hook it must be used within a component rendered by a Data router. See Picking a Router.
Example:
const MyComponent = (props) => {
const [showPrompt, setShowPrompt] = useState(false);
...
return (
...
<Prompt
when={showPrompt}
message="Unsaved changes detected, continue?"
beforeUnload={true}
/>
);
}
import {
createBrowserRouter,
createRoutesFromElements,
Route,
RouterProvider,
} from "react-router-dom";
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<Root />}>
{/* ... etc. */}
<Route path="myComponent" element={<MyComponent />} />
{/* ... etc. */}
</Route>
)
);
const App = () => <RouterProvider router={router} />;

How to use useSearchParams Hook with React Router v6

I am trying to implement a search parameter functionality to my React image search app. And, I have learned that I need to (can) use the useSearchParams Hook, but I am not sure how to make these changes.
So, basically I want the URL to be something like localhost:3000/input&page=1, meaning that whatever comes after the slash is going to be the input value and key/value pair for page numbers.
As you can see in the app.js, I have these 3 main Routes and the Home Route (renders Main.js) is the one I am mainly working on. Also, Main.js renders Header.js (renders form and others).
I am thinking that I should create a new Route in the app.js but I am not sure what to do.
import './App.css';
import Home from './components/pages/Home';
import Favorites from './components/pages/Favorites';
import Error from './components/pages/Error';
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { SkeletonTheme } from 'react-loading-skeleton';
import { useDarkMode } from './components/Navbar';
function App() {
const darkMode = useDarkMode(state => state.darkMode)
let style
if (darkMode === 'light') {
style = 'wrapper'
} else {
style = 'wrapper-dark'
}
return (
<div className={style}>
<SkeletonTheme baseColor="#808080" highlightColor="#b1b1b1">
<BrowserRouter>
<Routes>
<Route path='/' element={<Home />} />
<Route path='favorites' element={<Favorites />} />
<Route path='*' element={<Error />} />
</Routes>
</BrowserRouter>
</SkeletonTheme>
</div>
);
}
export default App;
import React from 'react'
import Header from './Header'
import Image from './Image'
import { useState, useEffect, useRef } from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faTriangleExclamation } from '#fortawesome/free-solid-svg-icons'
// import InfiniteScroll from 'react-infinite-scroll-component'
function Main() {
const [input, setInput] = useState('')
const [allImages, setAllImages] = useState([])
// const [totalResults, setTotalResults] = useState(null)
const [isVisible, setIsVisible] = useState(false)
const [error, setError] = useState(null)
const [showError, setShowError] = useState(false)
const [fadeOut, setFadeOut] = useState(false)
const [page, setPage] = useState(1)
const paginationRef = useRef(false)
// get
useEffect(() => {
if (localStorage.getItem('input')) {
setInput(JSON.parse(localStorage.getItem('input')))
}
if (localStorage.getItem('allImages')) {
setAllImages(JSON.parse(localStorage.getItem('allImages')))
// setTotalResults(JSON.parse(localStorage.getItem('totalResults')))
setIsVisible(JSON.parse(localStorage.getItem('isVisible')))
setPage(JSON.parse(localStorage.getItem('page')))
paginationRef.current = true
}
}, [])
// set
//* dryer?
useEffect(() => {
localStorage.setItem('input', JSON.stringify(input))
}, [input])
useEffect(() => {
localStorage.setItem('allImages', JSON.stringify(allImages))
}, [allImages])
// useEffect(() => {
// localStorage.setItem('totalResults', JSON.stringify(totalResults))
// }, [totalResults])
useEffect(() => {
localStorage.setItem('isVisible', JSON.stringify(isVisible))
}, [isVisible])
function handleChange(event) {
setInput(event.target.value)
}
// display nothing by default
// display image-list when user press search button
// function handleSubmit(event) {
// event.preventDefault()
// // interpolate input state and .env variable to API
// fetch(`https://api.unsplash.com/search/photos?query=${input}&client_id=${process.env.REACT_APP_UNSPLASH_API_KEY}`)
// .then(res => res.json())
// .then(data => setAllImages(data.results))
// }
async function fetchImages() {
try {
const res = await fetch(`https://api.unsplash.com/search/photos?&page=${page}&per_page=30&query=${input}&client_id=${process.env.REACT_APP_UNSPLASH_API_KEY}`)
const data = await res.json()
if (data.total !== 0) {
setAllImages(data.results)
// setTotalResults(data.total)
setIsVisible(true)
}
} catch(error) {
setError(error)
}
}
const handleSubmit = async (event) => {
event.preventDefault();
fetchImages()
setPage(1)
paginationRef.current = true
}
// error
useEffect(() => {
if (error) {
setShowError(true)
setTimeout(() => {
setFadeOut(true)
setTimeout(() => {
setShowError(false)
}, 1000)
}, 5000)
}
}, [error])
// total results
// let results
// if (totalResults >= 10000) {
// results = 'Total Results: ' + totalResults + '+'
// } else if (totalResults > 0) {
// results = 'Total Results: ' + totalResults
// } else if (totalResults === 0) {
// results = 'Nothing Found'
// }
// pagination
useEffect(() => {
if (paginationRef.current) {
fetchImages()
}
localStorage.setItem('page', JSON.stringify(page))
}, [page])
function handlePrev() {
setPage(prevState => prevState - 1)
fetchImages()
}
function handleNext() {
setPage(prevState => prevState + 1)
fetchImages()
}
return (
<main>
<Header
input={input}
handleChange={handleChange}
handleSubmit={handleSubmit}
/>
{showError && <div className={`network-error ${fadeOut ? 'fade-out' : ''}`}>
<i><FontAwesomeIcon icon={faTriangleExclamation} /></i>
<div className='network-error--message'>
<h5>Network Error</h5>
<p>Please check your Internet connection and try again</p>
</div>
</div>}
{/* <p className='main--results'>{results}</p> */}
<div className='main--image-list mt-5 pb-5'>
{allImages.map(el => (
<Image
key={el.id}
// do need spread operator below for img's src to work in Image.js
{...el}
el={el}
/>
))}
</div>
{isVisible && <div className='main--pagination'>
<button disabled={page === 1} onClick={handlePrev}>
Prev
</button>
<h5 className='main--pagination--h5'>{page}</h5>
<button onClick={handleNext}>
Next
</button>
</div>}
</main>
)
}
export default Main
import React from 'react'
import Navbar from './Navbar'
function Header(props) {
return (
<div className='header'>
<Navbar />
<h2 className='header--heading text-center text-light'>Find Images</h2>
<div className='header--form'>
<form onSubmit={props.handleSubmit}>
<input
className='header--form--input'
autoComplete='off'
type='text'
placeholder='Search'
onChange={props.handleChange}
name='input'
value={props.input}
/>
</form>
</div>
</div>
)
}
export default Header
If you are just wanting to initialize the page state to the page queryParam the the following could work. If uses the useSearchParams to access the queryString and return a constructed URLSearchParams object which can then access individual query params. Pass the "page" query param as the initial page state value.
const [searchParams] = useSearchParams();
const [page, setPage] = useState(Number(searchParams.get("page")) || 1);
In all likelihood though you'll not want competing "sources of truth" for what the current page is. If you want the URL queryString to be the source of truth then remove the page state and just read/update the "page` query parameter directly.
Example:
function Main() {
const [searchParams, setSearchParams] = useSearchParams();
...
const page = Number(searchParams.get("page"));
// get
useEffect(() => {
...
if (localStorage.getItem('allImages')) {
...
setSearchParams(params => {
params.set("page", JSON.parse(localStorage.getItem('page')) || 1);
return params;
});
...
}
}, []);
...
const handleSubmit = async (event) => {
event.preventDefault();
...
setSearchParams(params => {
params.set("page", 1);
return params;
});
...
}
...
// pagination
useEffect(() => {
if (paginationRef.current) {
fetchImages();
}
localStorage.setItem('page', JSON.stringify(page));
}, [page])
function handlePrev() {
setSearchParams(params => {
params.set("page", Math.max(1, page - 1));
return params;
});
...
}
function handleNext() {
setSearchParams(params => {
params.set("page", page + 1);
return params;
});
...
}
return (
...
);
}

Custom lazy import function is occured all file will be chunked under import path

In React Project, I was trying to dynamic lazy load with custom functions.
Why Custom function will be make all chunked files in container directory even if just declared not using it?
if I applied my custom lazyImport function in other code, results will be same. Doesn't care about the function is used that is enough just declared.
const lazyImport = (containerName: string) => lazy(() => import(`containers/${containerName}`));
const assignRouter: AssignRoute[] = Object.keys(routerMeta).map((componentKey: string) => {
const propsArr: propsArrTypes = assignRouteArrayProps(routerMeta[componentKey])
return {
Comp: lazyImport(componentKey),
propsArr
}
})
const assignRoute = (Comp: ComponentType<any>, props: RouteMetaProps) => {
return <Route key={props.path} element={<Comp />} {...props} />
}
const CommonRouter: FunctionComponent<ICustomRotuerProps> = (props) => {
return <Suspense fallback={<FlexCenter>
<Spin />
</FlexCenter>}>
<Routes>
{assignRouter.map(({ Comp, propsArr }: AssignRoute) => {
return assignRoute(Comp, (propsArr as RouteMetaType) as RouteMetaProps)
}
})}
</Routes>
</Suspense>;
};
export default CommonRouter

Problem when dynamically registering routes in an application with microfrontends concept

I have an Typescript + Redux (with RTK) application using the microfrontends concept. All the steps for the construction came from this tutorial: Microfrontends tutorial.
The main component is Microfrontend.tsx (omitted imports):
interface Manifest {
files: {
'main.js': string
'main.js.map': string
'index.html': string
}
entrypoints: string[]
}
const MicroFrontend = ({
name,
host,
module
}: {
name: string
host: string | undefined
module: string
}) => {
const history = useHistory()
useEffect(() => {
const renderMicroFrontend = () => {
// #ts-ignore
window[`render${name}`] && window[`render${name}`](`${name}-container`, history)
}
if (document.getElementById(name)) {
renderMicroFrontend()
return
}
const manifestUrl = `${
isDevProfile ? host : ''
}/${module}/view/asset-manifest.json`
fetch(manifestUrl)
.then(res => res.json())
.then((manifest: Manifest) => {
const script = document.createElement('script')
script.id = name
script.crossOrigin = ''
script.src = `${host}${manifest.files['main.js']}`
script.onload = () => {
renderMicroFrontend()
}
document.head.appendChild(script)
})
return () => {
// #ts-ignore
window[`unmount${name}`] && window[`unmount${name}`](`${name}-container`)
}
})
return (
<main id={`${name}-container`} style={{ height: '100%' }} />
)
}
MicroFrontend.defaultProps = {
document,
window
}
export default MicroFrontend
I'm trying to render the routes of the child components in a dynamic way, however, when I do this, I have a very strange effect: Bug.
The code snippet that generates this effect is this (omitted imports):
const App = () => {
const dispatch = useAppDispatch()
const { loadWithSuccess } = useSelector(moduleSelectors)
const avaibleModuleLinks = useSelector(avaibleModuleLinksWhitoutHome)
useEffect(() => {
dispatch(fetchAvaibleModules()).then(response =>
dispatch(fetchAvaibleModuleLinks(response.payload as string[]))
)
}, [dispatch])
return (
<BrowserRouter>
<Template>
<Switch>
<Route exact={true} path="/" component={Home} />
{loadWithSuccess ? avaibleModuleLinks?.map(
(subMenuPath: SubMenuPath | undefined, index: number) => {
const subMenuPathKey = subMenuPath ? subMenuPath.key : ''
let micro = () => (
<MicroFrontend
module={subMenuPathKey}
host="127.0.0.1"
name={subMenuPath ? subMenuPath.key.charAt(0).toUpperCase() : ''}
/>
)
return (
<Route
key={index}
path={`/dfe/view/${subMenuPathKey}`}
component={micro}
/>
)
}
): <></>}
</Switch>
</Template>
</BrowserRouter>
)
}
export default App
Only when I don't render routes dynamically do I have the desired effect: desired behavior
The code snippet that generates this effect is this (omitted imports):
const ModuleNfe = () => (
<MicroFrontend host="127.0.0.1" name="Nfe" module="nfe" />
)
const App = () => {
const dispatch = useAppDispatch()
const { loadWithSuccess } = useSelector(moduleSelectors)
const avaibleModuleLinks = useSelector(avaibleModuleLinksWhitoutHome)
useEffect(() => {
dispatch(fetchAvaibleModules()).then(response =>
dispatch(fetchAvaibleModuleLinks(response.payload as string[]))
)
}, [dispatch])
return (
<BrowserRouter>
<Template>
<Switch>
<Route exact={true} path="/" component={Home} />
<Route path="/dfe/view/nfe" component={ModuleNfe} />
</Switch>
</Template>
</BrowserRouter>
)
}
export default App
As you may have noticed, the desired behavior is for my page to be rendered inside the Template component. But for some reason, this is not the case.

Uncaught (in promise) TypeError: Cannot read property 'fields' of undefined (React + Contentful)

I'm trying to create a "portfolio" website to learn react. I've plugged content from Contentul, but i'm getting an error : Uncaught (in promise) TypeError: Cannot read property 'fields' of undefined when trying to display my content.
Here's what i've done so far to get content from Contentful into my React app :
I've creacted a contentful.js file
## contentful.js
const client = require('contentful').createClient({
space: 'MYSPACEID',
accessToken: 'MYACCESSTOKEN',
});
const getProjectItems = () => client.getEntries().then((response) => response.items);
const getSingleProject = (slug) =>
client
.getEntries({
'fields.slug': slug,
content_type: 'project',
})
.then((response) => response.items);
export { getProjectItems, getSingleProject };
Then, i've created 2 custom Hooks for getting my content :
## UseProjects.js
import { useEffect, useState } from 'react';
import { getProjectItems } from '../contentful';
const promise = getProjectItems();
export default function useProjects() {
const [projects, setProjects] = useState([]);
const [isLoading, setLoading] = useState(true);
useEffect(() => {
promise.then((project) => {
setProjects(project);
setLoading(false);
});
}, []);
return [projects, isLoading];
}
## useSingleProject.js
import { useEffect, useState } from 'react';
import { getSingleProject } from '../contentful';
export default function useSingleProject(slug) {
const promise = getSingleProject(slug);
const [project, setProject] = useState(null);
const [isLoading, setLoading] = useState(true);
useEffect(() => {
promise.then((result) => {
setProject(result[0].fields);
setLoading(false);
});
}, [promise]);
return [project, isLoading];
}
I can add my components code if needed but i feel like my error comes from here.. What's weird is that if i close the error, i see all the items properly rendered (so..they're properly pulled from Contentful) and if i click on it i've got the correct informations displayed (title, image, etc.). But the error makes weird layout things.
The error comes from my useSingleProject.js file (useSingleProject.js:13)
Now here i feel it can also come from my App.js file, i'm not sure about how i configured the routing for single project pages (i'm still new to react..). If i disable the following line from the routes array : { path: '/:id', name: ':id', Component: SingleProject }, then the error disapears. I can see all the projects on my projects page, but if i click on one of them the slug changes but nothing shows on the single project pages, since i've disabled it.
## App.js
import React, { useEffect } from 'react';
import { Route } from 'react-router-dom';
import { gsap } from 'gsap';
import './styles/App.scss';
import Header from './components/header';
import Navigation from './components/navigation';
import CaseStudies from './pages/caseStudies';
import Approach from './pages/approach';
import Services from './pages/services';
import About from './pages/about';
import Home from './pages/home';
import Projects from './pages/projects';
import SingleProject from './pages/SingleProject';
const routes = [
{ path: '/', name: 'Home', Component: Home },
{ path: '/case-studies', name: 'caseStudies', Component: CaseStudies },
{ path: '/approach', name: 'approach', Component: Approach },
{ path: '/services', name: 'services', Component: Services },
{ path: '/about-us', name: 'about', Component: About },
{ path: '/projects', name: 'projects', Component: Projects },
{ path: '/:id', name: ':id', Component: SingleProject },
];
function debounce(fn, ms) {
let timer;
return () => {
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
fn.apply(this, arguments);
}, ms);
};
}
function App() {
const [dimensions, setDimensions] = React.useState({
height: window.innerHeight,
width: window.innerWidth,
});
useEffect(() => {
// prevents flashing
gsap.to('body', 0, { css: { visibility: 'visible' } });
const debouncedHandleResize = debounce(function handleResize() {
setDimensions({
height: window.innerHeight,
width: window.innerWidth,
});
}, 1000);
window.addEventListener('resize', debouncedHandleResize);
return () => {
window.removeEventListener('resize', debouncedHandleResize);
};
});
return (
<>
<Header dimensions={dimensions} />
<div className="App">
{routes.map(({ path, Component }) => (
<Route key={path} exact path={path}>
<Component dimensions={dimensions} />
</Route>
))}
</div>
<Navigation />
</>
);
}
export default App;
EDIT :
So i've tried to console.log(response.items) in my getSingleProject function. It returns the correct array of object (so here containing only one object).
I've also tried tu console.log(result) in my useProjects function (inside the useEffect). It still logs the correct object, and it has the fields property i need to get. When console logging in my useEffect, it logs the object every second or so by the way. Is this a normal behavior?
Your hook useSingleProject is coded in such a way that it triggers an infinite re-render loop. Here is a reproduction of the behaviour I describe: https://codesandbox.io/s/confident-banach-tr609?file=/src/index.js. Observe how the console logs forever.
This happens because the hook useSingleProject is going to be called on each re-render, creating a new promise each time. In turn useEffect will trigger when promise is changed, causing an eventual state update, which also means an eventual re-render. This causes an infinite loop.
The solution would be to make your effect to depend on slug instead of promise. In fact, you don't even need to assign the promise to a variable first.
Also, just in case, it makes sense to only set isLoading to true only if you actually had to fetch data. So:
export default function useSingleProject(slug) {
const [project, setProject] = useState(null);
const [isLoading, setLoading] = useState(false);
useEffect(() => {
setLoading(true)
getSingleProject(slug).then((result) => {
setProject(result[0].fields);
setLoading(false);
});
}, [slug]);
return [project, isLoading];
}
So after another day trying to understand where the error comes from, it looks like it's coming from the way i've setup my Router in App.js. By modifying it, the error disappeared and now all content is pulled and displayed correctly. For those interested, here is my new App.js file and how i've setup routes for now to correct the issue :
import React, { useEffect } from 'react';
import { Switch, Route } from 'react-router-dom';
import { gsap } from 'gsap';
import './styles/App.scss';
import Header from './components/header';
import Navigation from './components/navigation';
import Approach from './pages/approach';
import Services from './pages/services';
import About from './pages/about';
import Home from './pages/home';
import Projects from './pages/projects';
import SingleProject from './pages/SingleProject';
function debounce(fn, ms) {
let timer;
return () => {
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
fn.apply(this, arguments);
}, ms);
};
}
function App() {
const [dimensions, setDimensions] = React.useState({
height: window.innerHeight,
width: window.innerWidth,
});
useEffect(() => {
// prevents flashing
gsap.to('body', 0, { css: { visibility: 'visible' } });
const debouncedHandleResize = debounce(function handleResize() {
setDimensions({
height: window.innerHeight,
width: window.innerWidth,
});
}, 1000);
window.addEventListener('resize', debouncedHandleResize);
return () => {
window.removeEventListener('resize', debouncedHandleResize);
};
});
return (
<>
<Header dimensions={dimensions} />
<div className="App">
<Switch>
<Route path="/" exact render={(props) => <Home {...props} dimensions={dimensions} />} />
<Route
path="/projects"
exact
render={(props) => <Projects {...props} dimensions={dimensions} />}
/>
<Route
path="/approach"
exact
render={(props) => <Approach {...props} dimensions={dimensions} />}
/>
<Route
path="/services"
exact
render={(props) => <Services {...props} dimensions={dimensions} />}
/>
<Route
path="/about-us"
exact
render={(props) => <About {...props} dimensions={dimensions} />}
/>
<Route
path="/:id"
render={(props) => <SingleProject {...props} dimensions={dimensions} />}
/>
</Switch>
</div>
<Navigation />
</>
);
}
export default App;
I'll refactor to make it less repetitive but here's how i've fixed it for now.

Resources