React-router-dom does not scroll to top of page - reactjs

I'm going crazy, I'm using react-router-dom, the moment I go from page A to B I want it to start at the top of the page, everywhere. I've tried different things like:
componentDidUpdate(){
console.log('hello');
document.documentElement.scrollTo(0, 0)
}
&
componentDidUpdate(){
console.log('hello');
window.scrollTo(0, 0)
}
&
import { useEffect } from 'react';
import { withRouter } from 'react-router-dom';
function ScrollToTop({ history }) {
useEffect(() => {
const unlisten = history.listen(() => {
window.scrollTo(0, 0);
});
return() => {
unlist();
}
}, []);
return(null);
}
export default withRouter(ScrollToTop);
But unfortunately without success, is there anyone who can tell me what I can do? I'm using :react": "^16.14.0"
So the intention is if I click on a <Link to={'../shopping cart'}>Shopping cart</Link> I will end up at the top of the page!

maybe you can use useRef hook. something like this:
const linkRef = useRef(null);
useEffect(() => {
if (linkRef.current) {
linkRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}, []);
return <Link ref={linkRef} to='/shopping-card' />

Related

history.listen() replacement in react router V6

I'm upgrading react router V5 application to react router v6. I have come across some snippet which I'm not able to convert to V6. I tried enough and looked at different solutions over internet but nothing seem to work for me.
Code that I want to convert to V6,
import React, { useRef, useEffect } from "react";
import { useHistory } from "react-router-dom";
export default () => {
const ref = useRef(null);
const history = useHistory(); // the browser history object
useEffect(() => {
const { onParentNavigate } = mount(ref.current, {
onNavigate: ({ pathname: nextPathname }) => {
const { pathname } = history.location;
if (pathname !== nextPathname) {
history.push(nextPathname); //<<<<< how to convert this to v6 ?
}
},
});
history.listen(onParentNavigate); //<<<<<< how to convert this to v6 ?
}, []);
return <div ref={ref} />;
};
MyVersion using V6
const ref = useRef(null);
// const navigation = useContext(UNSAFE_NavigationContext).navigator as BrowserHistory();
const navigate = useNavigate(); // V6
const location = useLocation(); // V6
useEffect(() => {
const { onParentNavigate } = dashboardMount(ref.current, {
onNavigate: (result: any) => {
const { pathname } = location;
if (pathname !== result.location.pathname) {
navigate(result.location.pathname, { replace: true }); // This is not throwing any error but not giving expected outcome
}
}
})
navigate(onParentNavigate); // This not throwing any error but not giving me expected outcome as well
}, []);
return <div ref={ref}></div>
Any pointer or solution will be highly appreciated !
You could likely use the UNSAFE_NavigationContext exported from react-router-dom to access the raw "history" navigator object.
///////////////////////////////////////////////////////////////////////////////
// DANGER! PLEASE READ ME!
// We provide these exports as an escape hatch in the event that you need any
// routing data that we don't provide an explicit API for. With that said, we
// want to cover your use case if we can, so if you feel the need to use these
// we want to hear from you. Let us know what you're building and we'll do our
// best to make sure we can support you!
//
// We consider these exports an implementation detail and do not guarantee
// against any breaking changes, regardless of the semver release. Use with
// extreme caution and only if you understand the consequences. Godspeed.
///////////////////////////////////////////////////////////////////////////////
/** #internal */
export {
UNSAFE_NavigationContext,
UNSAFE_LocationContext,
UNSAFE_RouteContext,
} from "react-router";
Example:
import React, { useContext, useRef, useEffect } from "react";
import { UNSAFE_NavigationContext } from "react-router-dom";
export default () => {
const ref = useRef(null);
const { navigator } = useContext(UNSAFE_NavigationContext); // the browser history object
useEffect(() => {
const { onParentNavigate } = mount(ref.current, {
onNavigate: ({ pathname: nextPathname }) => {
const { pathname } = navigator.location;
if (pathname !== nextPathname) {
navigator.push(nextPathname);
}
},
});
const unlisten = navigator.listen(onParentNavigate);
return unlisten; // <-- cleanup listener on component unmount
}, []);
return <div ref={ref} />;
};

Unable to clean event listener in React

Problem with removing addEventListener using useEffect return
I also tried to add empty array as a second argument to an useEffect, listener still remains
import React, { useCallback, useEffect } from "react";
import headerLogo from "./icons/logo.svg";
import { useHistory } from "react-router-dom";
function Home({ header }) {
const history = useHistory();
useEffect(() => {
const mouseClickListener = (e) => {
e.preventDefault();
console.log("LISTENING");
history.push("/dashboard");
};
window.addEventListener("click", mouseClickListener);
return () => window.removeEventListener("click", mouseClickListener);
});
return (
<div className={`${header ? "header-logo" : "home-logo"}`}>
<img src={headerLogo} alt="logo" />
</div>
);
}
export default Home;
That was all about, routing. Home component was rendering in another routes also and useEffect was setting event listener each time.
what I did was adding condition by wrapping listener functionality inside useEffect
useEffect(() => {
if (!header) {
const mouseClickListener = (e) => {
e.preventDefault();
console.log("LISTENING");
history.push("/dashboard");
};
window.addEventListener("click", mouseClickListener);
return () => window.removeEventListener("click", mouseClickListener);
}
});

how to prevent useEffect on the previous is screen

I have 2 screens, on my first screen I have a useEffect which has a watcher on a redux state.
Screen 1
...
useEffect(() => {
if (props.data) {
navigation.navigate('To Other Screen');
}
}, [props.data]);
...
Then on my second Screen.
I have a function that will update the redux 'data'.
Then, the useEffect on the previous was triggered.
How will I prevent this one.
Thank you.
I found this hoc on the react-nativgation and combine this with useEffect;
https://reactnavigation.org/docs/1.x/with-navigation-focus/
import { withNavigationFocus } from 'react-navigation';
...
const Page = (props) => {
useEffect(() => {
if (props.isFocused) {
// function call;
}
}, [props.isFocused]);
};
export default withNavigationFocus(Page);
Try this,
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
if(props.data){
navigation.navigate('To Other Screen')
}
})
return unsubscribe
}, [navigation, props.data])

How to add facebook Pixel on Next.js react app?

I know this may be a dumb question.
How can I do to use facebook pixel on a next.js react app ?
there are no dumb questions.
You can see nextjs example about how implements fb pixel.
Nextjs Facebook Pixel
Solution with typescript and hook with NextJs
Install react-facebook-pixel yarn add react-facebook-pixel
In your file _app.tsx
// pages/_app.tsx
import { useEffect } from 'react'
import { useRouter } from 'next/router'
const App = ({ Component, pageProps }) => {
const router = useRouter()
useEffect(() => {
import('react-facebook-pixel')
.then((x) => x.default)
.then((ReactPixel) => {
ReactPixel.init('XXXXXXXXXXXXXXXXXXXXX') // facebookPixelId
ReactPixel.pageView()
router.events.on('routeChangeComplete', () => {
ReactPixel.pageView()
})
})
}, [router.events])
return <Component {...pageProps} />
}
export default App
Remark: it works with typescript or JavaScript
Use the new Script component released in Next.js version 11. Import the below into your _app.js.
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import {pageview, FB_PIXEL_ID} from '../../lib/fpixel'
import Script from 'next/script'
const handleRouteChange = () => {
pageview()
}
const FB_PIXEL_ID = process.env.NEXT_PUBLIC_FACEBOOK_PIXEL_ID
const pageview = () => {
window.fbq('track', 'PageView')
}
const FacebookPixel = ({ children }) => {
const router = useRouter()
useEffect(() => {
// the below will only fire on route changes (not initial load - that is handled in the script below)
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [router.events])
return (
<Script id="facebook-pixel">
{`
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', ${FB_PIXEL_ID});
fbq('track', 'PageView');
`}
</Script>
)
}
export default FacebookPixel
UPDATE
Do not use strategy="lazyOnload". I was previously using this and the script was more likely to be blocked by adblocker if using this method.
There's a library for React called react-facebook-pixel. In order to make it work with NextJs, try this solution in your _app.jsx file:
function FacebookPixel() {
React.useEffect(() => {
import("react-facebook-pixel")
.then((x) => x.default)
.then((ReactPixel) => {
ReactPixel.init('pixel ID here');
ReactPixel.pageView();
Router.events.on("routeChangeComplete", () => {
ReactPixel.pageView();
});
});
});
return null;
}
export default function App({ Component, pageProps }) {
return (
<>
<Head>
<meta charSet="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, viewport-fit=cover"
/>
</Head>
<FacebookPixel />
//…
<main className="routesContainer">
<Component siteData={siteData} {...pageProps} />
</main>
//…
</>
);
}
or in case you're using Class components, insert this in you componentDidMount() inside the App class:
componentDidMount() {
import('react-facebook-pixel')
.then((x) => x.default)
.then((ReactPixel) => {
ReactPixel.init('Pixel ID Here');
ReactPixel.pageView();
Router.events.on('routeChangeComplete', () => {
ReactPixel.pageView();
});
});
}
font: https://github.com/zsajjad/react-facebook-pixel/issues/53
Simple and easy:
Put the below code inside _app.js:
useEffect(async () => {
const { default: ReactPixel } = await import('react-facebook-pixel');
ReactPixel.init(FB_PIXEL, null, {
autoConfig: true,
debug: true,
});
ReactPixel.pageView();
ReactPixel.track("ViewContent")
});

React hooks - remove from react-router location state when we refresh page

When I am entering one page of the app I pass data through location state using react-router. Then I access it via location.state.myDataObject. When I refresh the page it is still there, while I would like it to be empty. Here's what I've try to use:
const resetLocation = () => {
history.replace({
...location,
state: undefined,
});
};
useEffect(() => {
window.addEventListener('onbeforeunload', resetLocation);
}, []);
Or adding it to unmount action within useEffect but I guess it is not called when refreshing the page:
useEffect(() => {
return function cleanLocationState() {
history.replace({
...this.props.location,
state: undefined,
});
};
}, []);
I think this is the desired behavior of react router. If you want to reset the state then you need to do something like
import React, { useEffect, useCallback } from "react";
import { useLocation, useHistory } from "react-router-dom";
function Home() {
const location = useLocation();
const history = useHistory();
const replaceHistory = useCallback(() => {
history.replace({ ...location, state: undefined });
}, [history]);
useEffect(() => {
window.addEventListener("beforeunload", () => replaceHistory);
return () => {
window.removeEventListener("beforeunload", replaceHistory);
};
}, []);
return (
<div>
<h2>Home</h2>
</div>
);
}
export default Home;
Working example
How about you try the contrary? Store the value on component did mound and delete it from the location. I'm not sure that this is the prettiest solution, but i guess it's the easiest
const [state,setState]=useState();
useEffect(()=>{
setState(location.state);
location.state=undefined;
}, [location])
Try this way:
import React, { useEffect } from "react";
import { useHistory } from "react-router-dom";
function Home() {
const history = useHistory();
function replaceHistory(e) {
if (e) {
e.preventDefault();
delete e.returnValue;
}
history.replace({ ...history.location, state: undefined });
}
console.log("history", history.location);
useEffect(() => {
window.addEventListener("beforeunload", () => replaceHistory);
return () => {
// Reset Location state if we leave this page
replaceHistory();
window.removeEventListener("beforeunload", replaceHistory);
};
}, []);
return (
<div>
<h2>Home</h2>
</div>
);
}
export default Home;
CodesandBox Demo
The default behavior of the react-router will not save the history state after refresh the page so we need to know more about your code to really solve this issue. However, if the state do save, byour first attempt seem to have some flaw by using the history and location of window instead from the props.
function Page(props){
useEffect(() => {
const unloadFunc = () => {
//use the history and the location from the props instead of window
props.history.replace({
...props.location,
state: undefined,
});
}
window.addEventListener('onbeforeunload',unloadFunc);
return ()=>{
window.removeEventListener('onbeforeunload' unloadFunc);
//make history and location as the dependencies of the hook
}, [props.history, props.location]);
return <div></div>
}

Resources