React router page load always keep the scrolling position same. Is there a simple way to keep the scroll position in a certain position of the page?
useEffect(() => {
let unlisten = history.listen(({ location, action }) => {
console.log(action, location.pathname, location.state);
window.scrollTo(0, 0)
});
return () => unlisten()
}, [])
You can listen to history change and scroll to top, or any other position you want, at history change. You can keep this code in your first, say App, component.
Related
I am using laravel-websockets to listen to event. I have no issue on the back-end side; The issue is on the front-end side.
SCENARIO:
When I go to a specific route post/[slug], the current channel is based on the current slug. When I redirect to the same route but different value of slug, the channel listens to the first value on page refresh and not to the current one.
const Component = () => {
const router = useRouter();
const {slug} = router.query;
useEffect(() => {
window.Echo.private(`post.${slug}`).listen('PrivateEvent', e => {
console.log(e)
});
}, [slug])
}
Example:
On page refresh, go to post/first-slug. Next, click to <Link to="/post/second-slug">About</Link>
The example above should listen to second-slug and not the first-slug.
How can I solve this without hard refresh or <a> tag?
You forgot to stop listening on the previous channel, so the events are still received. I suppose that you end up with two active channels, receiving events for both.
Inside a useEffect() you should return a cleanup function that clears resources created for the effect
Here is how:
const Component = () => {
const router = useRouter();
const {slug} = router.query;
useEffect(() => {
window.Echo.private(`post.${slug}`).listen('PrivateEvent', e => {
console.log(e)
});
return () => window.Echo.private(`post.${slug}`).stopListening('PrivateEvent');
}, [slug])
}
If this does not solve your problem, please:
display the slug in your component (return <div>slug</div>;) to confirm that the navigation really happens ;
show us the whole console log.
I looked around and tried to find a solution with React router.
With V5 you can use <Promt />.
I tried also to find a vanilla JavaScript solution, but nothing worked for me.
I use React router v6 and histroy is replaced with const navigate = useNavigation() which doesn't have a .listen attribute.
Further v6 doesn't have a <Promt /> component.
Nevertheless, at the end I used useEffect clear function. But this works for all changes of component. Also when going forward.
According to the react.js docs, "React performs the cleanup when the component unmounts."
useEffect(() => {
// If user clicks the back button run function
return resetValues();;
})
Currently the Prompt component (and usePrompt and useBlocker) isn't supported in react-router-dom#6 but the maintainers appear to have every intention reintroducing it in the future.
If you are simply wanting to run a function when a back navigation (POP action) occurs then a possible solution is to create a custom hook for it using the exported NavigationContext.
Example:
import { UNSAFE_NavigationContext } from "react-router-dom";
const useBackListener = (callback) => {
const navigator = useContext(UNSAFE_NavigationContext).navigator;
useEffect(() => {
const listener = ({ location, action }) => {
console.log("listener", { location, action });
if (action === "POP") {
callback({ location, action });
}
};
const unlisten = navigator.listen(listener);
return unlisten;
}, [callback, navigator]);
};
Usage:
useBackListener(({ location }) =>
console.log("Navigated Back", { location })
);
If using the UNSAFE_NavigationContext context is something you'd prefer to avoid then the alternative is to create a custom route that can use a custom history object (i.e. from createBrowserHistory) and use the normal history.listen. See my answer here for details.
Is there a way to check the next location via React Router? I am not interested in using Prompt solution. My goal is to set some condition on component unmount like:
useEffect(() => {
return () => {
if(nextRoute !== '/something') {
resetData();
}
};
}, []);
Is there a way to have a knowledge what is the nextRoute?
A react app using hooks. In useEffect there is an api-call to populate content on page.
Each element fetched is clickable. And a click will open details for the clicked element in a new browser window/tab.
When working in the new window, the user can make changes on that object. The user will then close that tab or at least just go back to the main window/tab.
Question is how I can detect that the user is coming back to the main window. This is because I want to re-fetch data from API. Thus, I want to rerun useEffect.
While googling I found this:
https://www.npmjs.com/package/react-page-visibility
Is that really what I'm looking for? Reading the docs I'm not really sure if that can be the solution to my issue. Is there another way to solve this?
You can use the visibilitychange event to achieve that:
const onVisibilityChange = () => {
if (document.visibilityState === 'visible') {
console.log("Tab reopened, refetch the data!");
}
};
useLayoutEffect(() => {
document.addEventListener("visibilitychange", onVisibilityChange);
return () => document.removeEventListener("visibilitychange", onVisibilityChange);
}, []);
Codesandbox
With React 18 you can use 'visibilitychange' with react brand new hook useSyncExternalStore
export function useVisibilityChange(serverFallback: boolean) {
const getServerSnapshot = () => serverFallback;
const [getSnapshot, subscribe] = useMemo(() => {
return [
() => document.visibilityState === 'visible',
(notify: () => void) => {
window.addEventListener('visibilitychange', notify);
return () => {
window.removeEventListener('visibilitychange', notify);
};
},
];
}, []);
Gist with hook
P.S:Don't forget cross-browser usage
Trying to get the preloader to show, fade out, and then show the new route. How to do this? Right now the page is flashing, then the preloader shows, and fades out to the new route.
I'm trying to use useLocation with useEffect(() => {...}, [location]) but it's causing the flash.
At the router level I have it add a loading class to the body so the preloader shows. Once the route component is mounted I'm removing that class. It seems like the useEffect that triggers on location change is getting called after the route render though.
Alright so this seems to have fixed it here is the code I had before.
useEffect(() => {
document.body.classList.remove('loaded');
window.scrollTo(0, 0);
const currentPath = location.pathname + location.search;
analytics.sendPageview(currentPath);
setTimeout(function () {
document.body.classList.add("loaded");
}, 1000);
}, [location]);
Here is the code I switched to that fixed it.
useEffect(() => {
return history.listen(location => {
document.body.classList.remove('loaded');
setTimeout(function () {
document.body.classList.add("loaded");
}, 1000);
})
}, [history])