Reputation: 2290
I am looking for a solution to register the route change and apply new state
using setState
and useEffect
. The current code below doesn't update functions of setState
when the route is changed.
For example, I register the pathname
of /
with location.pathname === '/'
within createContext
, if the pathname
is /
the setState
of isHome
is registered true
, however if pathname
is /page-1
setState
is registered false
.
On browser reloads, onMount
the state
is correctly set, however on a route change using Link
this does not. Also, please note that I am using Gatsby and in doing so, importing { Link } from 'gatsby'
CreateContext.js
export const GlobalProvider = ({ children, location }) => {
const prevScrollY = useRef(0);
const [state, setState] = useState({
isHome: location.pathname === '/',
// other states
});
const detectHome = () => {
const homePath = location.pathname === '/';
if (!homePath) {
setState(prevState => ({
...prevState,
isHome: false
}));
}
if (homePath) {
setState(prevState => ({
...prevState,
isHome: true
}));
}
};
useEffect(() => {
detectHome();
return () => {
detectHome();
};
}, [state.isHome]);
return (
<GlobalConsumer.Provider
value={{
dataContext: state,
}}
>
{children}
</GlobalConsumer.Provider>
);
};
If I console.log(state.isHome)
on pathname
/
I get true
, any other pathnames I get false
, however, if I change route, the current isHome
state remains previous, until I scroll and useEffect
applies.
The point of registering the isHome
state is to alter CSS per page.
How can I update state with useEffect
when changing route. Previously, I would have done this with componentDidUpdate
and register prevProps.location.pathname
against props.location.pathname
, however, my understanding is that this is no longer necessary with the useEffect
hook.
Upvotes: 7
Views: 19538
Reputation: 12691
The effect you want is "When the location change then update my state", this is translated in useEffect
code like this :
useEffect(() => {
detectHome();
return () => {
detectHome();
};
}, [location]);
Upvotes: 5
Reputation: 1240
I think if you use GlobalProvider
as root component, in this case, it only render one time unless something change the states or props. So some explanation:
useEffect(() => {
detectHome();
return () => {
detectHome();
};
}, [state.isHome]);
This code above, the state
is updated by only this useEffect
, so it only update the state one time after first render, and the detectHome
inside of return of useEffect
only execute when other update happen or the state.isHome
is different of first time. It's little confused this explanation, but that is.
But for the solution is use the 'popstate' event of window:
export const GlobalProvider = ({ children, location }) => {
const prevScrollY = useRef(0);
const [state, setState] = useState({
isHome: location.pathname === '/',
// other states
});
const detectHome = () => {
const homePath = location.pathname === '/';
if (!homePath) {
setState(prevState => ({
...prevState,
isHome: false
}));
}
if (homePath) {
setState(prevState => ({
...prevState,
isHome: true
}));
}
};
useEffect(() => {
window.addEventListener('popstate', detectHome)
return () => {
window.removeEventListener('popstate', detectHome)
};
}, [state.isHome]);
return (
<GlobalConsumer.Provider
value={{
dataContext: state,
}}
>
{children}
</GlobalConsumer.Provider>
);
};
I think that Gatsby should manipulate the history, so this will fire popstate
of browser, and you can detect the url's changes.
Upvotes: 0
Reputation: 568
If you are using react-router you can subscribe to location change event in your useEffect:
import {browserHistory} from 'react-router';
...
useEffect(() => {
return browserHistory.listen(detectHome);
}, []);
...
This would subscribe your detectHome
function for location change on mount and unsubscribe it on unmount.
Upvotes: 1