React redirect from a function inside App component - reactjs

I have a function inside AppComponent as shown below.
login = () => {
this.setState({
isLoggedIn : true
}, () => {
localStorage.setItem("isLoggedIn", true);
// here i have to redirect to a different page
// I dont have access to this.props.history since this function is inside AppComponent
});
}
I want to redirect to a different page. But I dont have access to props.history as this function is declared within the Appcomponent itself.
how can I make the function to redirect to a different component after updating the state.
Help much appreaciated.

Related

Handling Router events inside class - React

So I have a class, which was previously using NextJs like:
class Handler {
constructor() {
Router.events.on('routeChangeComplete', this.routeHandle)
}
routeHandle = (url) => { // some code }
unmount() {
Router.events.off('routeChangeComplete', this.routeHandle)
}
}
export default new Handler()
Whenever this class is called, the Router.events.on('routeChangeComplete', this.routeHandle) inside the constructor somehow gets triggered with the right URL (I don't understand how or why bc there's no param there but anyway).
My question is how can I replace Router.events.on('routeChangeComplete', this.routeHandle), because now I'm not using NextJS anymore, I'm only using React, which I understand that Router is a part of.

React useEffect strange behaviour with custom layout component

I'm trying to use scroll position for my animations in my web portfolio. Since this portfolio use nextJS I can't rely on the window object, plus I'm using navigation wide slider so I'm not actually scrolling in the window but in a layout component called Page.
import React, { useEffect } from 'react';
import './page.css';
const Page = ({ children }) => {
useEffect(() => {
const scrollX = document.getElementsByClassName('page')
const scrollElement = scrollX[0];
console.log(scrollX.length)
console.log(scrollX)
scrollElement.addEventListener("scroll", function () {
console.log(scrollX[0].scrollTop)
});
return () => {
scrollElement.removeEventListener("scroll", () => { console.log('listener removed') })
}
}, [])
return <div className="page">{children}</div>;
};
export default Page;
Here is a production build : https://next-portfolio-kwn0390ih.vercel.app/
At loading, there is only one Page component in DOM.
The behaviour is as follow :
first listener is added at first Page mount, when navigating, listener is also added along with a new Page component in DOM.
as long as you navigate between the two pages, no new listener/page is added
if navigating to a third page, listener is then removed when the old Page is dismounted and a new listener for the third page is added when third page is mounted (etc...)
Problem is : when you navigate from first to second, everything looks fine, but if you go back to the first page you'll notice the console is logging the scrollX value of the second listener instead of the first. Each time you go on the second page it seems to add another listener to the same scrollElement even though it's not the same Page component.
How can I do this ? I'm guessing the two component are trying to access the same scrollElement somewhat :/
Thanks for your time.
Cool site. We don't have complete info, but I suspect there's an issue with trying to use document.getElementsByClassName('page')[0]. When you go to page 2, the log for scrollX gives an HTMLCollection with 2 elements. So there's an issue with which one is being targeted. I would consider using a refs instead. Like this:
import React, { useEffect, useRef } from 'react';
import './page.css';
const Page = ({ children }) => {
const pageRef = useRef(null)
const scrollListener = () => {
console.log(pageRef.current.scrollTop)
}
useEffect(() => {
pageRef.addEventListener("scroll", scrollListener );
return () => {
pageRef.removeEventListener("scroll", scrollListener )
}
}, [])
return <div ref={pageRef}>{children}</div>;
};
export default Page;
This is a lot cleaner and I think will reduce confusion between components about what dom element is being referenced for each scroll listener. As far as the third page goes, your scrollX is still logging the same HTMLElement collection, with 2 elements. According to your pattern, there should be 3. (Though there should really only be 1!) So something is not rendering properly on page 3.
If we see more code, it might uncover the error as being something else. If refs dont solve it, can you post how Page is implemented in the larger scope of things?
also, remove "junior" from the "junior developer" title - you won't regret it

Where to Put Code that should run First ReactJs + Mobx State Tree

I have some code that grabs the users ipAddres. I do this right now in my componentDidMount in my app.js
async componentDidMount() {
await eventTrackingStore.getIpAddress();
}
So I did it in my app.js as it is my root component and I only want to set this once. This works fine if the user starts from the home page and navigates through the site.
However some pages can be loaded directly(ie you type in the url in your browser and it goes straight to that page).
Since the react lifecycle starts with most immediate component, which calls a method that expects the ipAddress code to be set but it does not get set till it hits the app.js
Now I could put the above code in each method but that gets tedious. Is there some sort of method in reactjs, or mbox or mbox state tree that would fire first?
If you use mobx-state-tree and you have a global store, then that global store can make the API call in the afterCreate method
const AppModel = types.model({
ips: types.array(types.string)
}).actions(self => ({
afterCreate() {
flow(function*() {
const ips = yield eventTrackingStore.getIpAddress();
self.setIps(ips)
})()
},
setIps(ips: string[]) {
self.ips = ips
}
}))
OR
The same thing you can do in a wrapped react component that wrappes every page of your app.
class App extends React.Component {
componentDidMount() {
eventTrackingStore.getIpAddress().then(res => {
// set the ips into a store or any logic you want in order to pass them down to children
})
}
render() {
return this.props.children
}
}
I can think of two solutions:
You can use react context
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Use context to share the store between all components and if the data is not loaded, initialize loading right there in that nested component.
If the data is already there then just take it,
getIpAddress method should return a promise, so in case when data is already there it will be immediately resolved.

React: add body class only to certain components in useEffect hook?

I am using React 16.8.6 and hooks. I am new to hooks.
I have a component loaded in a route I need to add a body class to. When the user leaves this page, I need the class removed. I am using
useEffect(() => {
document.body.className = 'signin';
}, []);
This correctly adds the class to the body tag. Except when I navigate to another page, the class remains. If I reload the second page it's gone.
How do I remove the class when the component unmounts when the route changes?
If your effect returns a function, it will act as a cleanup.
useEffect(() => {
document.body.classList.add('signin');
return function cleanup() {
document.body.classList.remove('signin');
};
}, []);
You can check out Effects with cleanup in the documentation
The useEffect hook supports a cleanup function that runs when the component unmounts
useEffect(() => {
document.body.className = 'signin';
return () => { document.body.className = ''; }
});
See the docs.

React-Router <Link> to load a page, even if we're already on that page

I use react router in my website. I have homepage and contact.
When I am on homepage and I click on the homepage again. How can I reload the page like Facebook
issue here: https://github.com/ReactTraining/react-router/issues/1982
After looking at the issue, I can realize that you may use force update:
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
this.forceUpdate();
}
}
You could set up an event handler on the 'home' Link, that will fetch the data you want to be updated.
class Nav extends React.Component {
constructor () {
super()
this.handleClick = this.handleClick.bind(this)
}
handleClick () {
// code that fetches Home Page data
}
render () {
return (
<Link to='your/path' onClick={this.handleClick}>Home</Link>
)
}
}
This might be a common problem and I was looking for a decent solution to have in my toolbet for next time. React-Router provides some mechanisms to know when an user tries to visit any page even the one they are already.
Reading the location.key hash, it's the perfect approach as it changes every-time the user try to navigate between any page.
componentDidUpdate (prevProps) {
if (prevProps.location.key !== this.props.location.key) {
this.setState({
isFormSubmitted: false,
})
}
}
Reference: A location object is never mutated so you can use it in the lifecycle hooks to determine when navigation happens

Resources