Method not being executed when leaving a component with componentWillUnmount() - reactjs

I need to check between Facebook's expiration date and the current date when I enter in two different components. Dashboard and Pages. Both have this:
componentWillMount() {
const { linkedAccount, clearLinkedAccounts, getFacebookPages } = this.props
const now = moment().format('YYYY-MM-DD HH:mm:ss')
if (linkedAccount) {
if (now < linkedAccount.expiresIn) {
getFacebookPages()
} else {
clearLinkedAccounts()
}
}
}
componentWillUnmount() {
const { linkedAccount, clearLinkedAccounts } = this.props
const now = moment().format('YYYY-MM-DD HH:mm:ss')
if (linkedAccount && now > linkedAccount.expiresIn) {
clearLinkedAccounts()
}
}
But only when refreshing the page manually, the clearLinkedAccounts() is executed, by running componentWillMount, but before leaving it, it doesn't. I want it to be executed, if necessary, before entering in another route.

Refreshing the page is not like going from a page to another page inside the same single app context. React router allows you to go from a page to another within a React single app with tag like Link. It is why react router is so useful!
When you click on a link to go to another page with Link, the React single app is still active. As a result, componentWillUnmount wil lbe called because the single app is able to unmount component.
When you refresh the page (F5), a new page is loaded. As a result, the entire existing single app is removed and given to the browser garbage collector. In that case, you have no control on React component. So impossible to call componentWillUnmount because there is no more components at all.

Related

Changing Query paramers while staying on the same page without reload= NextJS Router

for a project I am working on I am running into a problem with the nextjs Router.I have a component that has an input field which the user should be able to input their searchterm in. There is a different component which should be able to get this searchterm and perform a search.
Because the two components aren't connected I would like to set the queryParameters in the router in the Input component, and then execute a function in the search component when the searchTerm is changed.
The problem lies in the following: The searchComponent receives the nextJS router as props and will only execute my useEffect function when those props are changed (and react knows they are changed), on top of that I need to stay on the same page when updating the query parameters, but the route of this page is dynamic. For example: the user can add this combination of components on /search but also on /lookforitem.
I have tried setting the queryParameters in the following way in the Input component:
function setQueryParams() {
router.query = {
...router.query,
searchTerm: input.current,
};
}
In combination with the following code in the Search component:
useEffect(() => {
console.log('Router has changed');
}, [router]);
The problem is that this useEffect doesnt get called untill the search component is rendered again (I have created a button that logs the router to the console, and it shows the updated router), which I assume is because React hasn't realised that the Router props have changed.
I have also tried setting the query parameters via a router.push in the following way:
function setQueryParams() {
router.push(
{
pathname: router.route,
query: {
...router.query,
searchTerm: input.current,
},
},
undefined,
{ shallow: true }
);
}
However this comes with its own set of problems. First of all it causes a refresh of the page, which I don't want. On top of that it changes the url to for example: /search?searchTerm=Hello which means that if I enter a different input and submit it will stack making the next url for example: &searchterm=hello?searchterm=goodbye.
I want a way to update the query parameters without refreshing the page, but while also notifying the other components that use the router that the query parameters have updated. All of the searching that I've done seems to be specific to either routing to a different page or routing to a predefined page.
Any help would be greatly appreciated.

mxKeyHandler not working when React component is remounted

I'm using mxGraph in a React Single Page Application and instantiating a new mxKeyHandler in componentDidMount. I also invoke its destroy method in componentWillUnmount. It all works fine the first time you visit the page that has the mxGraph component, but if I navigate away to a different page and come back (without a page refresh), it no longer works.
componentDidMount() {
this.editor = new mxEditor();
this.editor.setGraphContainer(this.mxGraphRef.current);
......
this.keyHandler = new mxKeyHandler(this.editor.graph);
this.keyHandler.bindKey(46, evt => {
this.editor.graph.removeCells();
});
}
componentWillUnmount() {
this.keyHandler.destroy();
this.editor.destroy();
}
Any advice as to what I'm doing wrong?
Ok, the solution was to add the following 2 lines:
this.editor.graph.container.setAttribute('tabindex', '-1');
this.editor.graph.container.focus();
As described here: https://jgraph.github.io/mxgraph/docs/known-issues.html#Focus

Preserve internal state on page refresh in React.js

It must be pretty regular issue.
I'm passing props down to the children and I'm using it there to request to the endpoint. More detailed: I'm clicking on the list item, I'm checking which item was clicked, I'm passing it to the child component and there basing on prop I passed I'd like to request certain data. All works fine and I'm getting what I need, but only for the first time, ie. when refreshing page incoming props are gone and I cannot construct proper URL where as a query I'd like to use the prop value. Is there a way to preserve the prop so when the page will be refresh it will preserve last prop.
Thank you!
(You might want to take a look at: https://github.com/rt2zz/redux-persist, it is one of my favorites)
Just like a normal web application if the user reloads the page you're going to have your code reloaded. The solution is you need to store the critical data somewhere other than the React state if you want it to survive.
Here's a "template" in pseudo code. I just used a "LocalStorage" class that doesn't exist. You could pick whatever method you wanted.
class Persist extends React.Component {
constuctor(props) {
this.state = {
criticalData = null
}
}
componentDidMount() {
//pseudo code
let criticalData = LocalStorage.get('criticalData')
this.setState({
criticalData: criticalData
})
}
_handleCriticalUpdate(update) {
const merge = {
...LocalStorage.get('criticalData')
...update
}
LocalStorage.put('criticalData', merge)
this.setState({
criticalData: merge
})
}
render() {
<div>
...
<button
onClick={e => {
let update = ...my business logic
this._handleCriticalUpdate(update) //instead of set state
}}
>
....
</div>
}
}
By offloading your critical data to a cookie or the local storage you are injecting persistence into the lifecycle of the component. This means when a user refreshes the page you keep your state.
I hope that helps!

Handle back button with react router

If a user navigates to www.example.com/one and clicks the back button, I want to redirect them to www.example.com.
I think it's a common problem, but I haven't found a solution yet.
Hooks version (React 16.8+):
Minimal version.
import { useHistory } from "react-router-dom";
export const Item = () => {
let history = useHistory();
return (
<>
<button onClick={() => history.goBack()}>Back</button>
</>
);
};
In react-router-dom v6 useHistory() is replaced by useNavigate(). so use useNavigate() inplace of useHistory() this way.
import { useNavigate} from "react-router-dom";
export const Item = () => {
let navigate = useNavigate();
return (
<>
<button onClick={() => navigate(-1)}>Back</button>
</>
);
};
for more on useNavigate visit this: https://reactrouter.com/docs/en/v6/hooks/use-navigate
You can try with two options, either you can use push method or goBack method from history of the router. Normally history props will available if you directly route the component via Route method or pass the history props to child component and use it.
Sample Code given below
this.props.history.push('/') //this will go to home page
or
this.props.history.goBack() //this will go to previous page
For your problem you try with push method and give the exact url you to move on.
For more reference visit https://reacttraining.com/react-router/web/api/history
What you want is this:
Let's say a person goes to a single page in your website such as: www.yoursite.com/category/books/romeo-and-juliet
In this page, you want to show a "Back" button that links you to one upper directory which is: www.yoursite.com/category/books/
This is breadcrumb system that we famously had in vBulletin forums and such.
Here is a basic solution to this:
let url = window.location.href;
let backButtonUrl = "";
if (url.charAt(url.length - 1) === "/") {
backButtonUrl = url.slice(0, url.lastIndexOf("/"));
backButtonUrl = backButtonUrl.slice(0, backButtonUrl.lastIndexOf("/"));
} else {
backButtonUrl = url.slice(0, url.lastIndexOf("/"));
}
What it basically does is:
1. Get the current URL from browser
2. Check if there is a "/" (slash) at the end of the link.
a. If there is: remove the slash, and remove everything the last slash
b. If there is not: remove everything last slash.
You can use {backButtonUrl} as your Go Back button link.
Note: it does not have anything to do with React Router, history, etc.
Note 2: Assuming you are using a link architecture that goes like www.site.com/word/letter/character
If you want to use it with react-router-dom library, then you need to set your url variable like this:
let url = this.props.match.url;
I found a solution. It's not beautiful but it works.
class Restaurant extends Component {
constructor(props) {
super(props);
this.props.history.push('/');
this.props.history.push(this.props.match.url);
}
...
I've had the same problem today. I have the following flow in one of the applications I'm working on:
User fills out a registration form
User enters credit card "payment page"
When payment is successful, the user sees a "payment confirmation" page.
I want to prevent the users from navigating from the "payment confirmation" (3) page back to any previous steps in the payment flow (1 and 2).
The best thing to do would be not to use routes to control which content is displayed, and use state instead. If you cannot afford to do that,
I found two practical ways to solve the problem:
Using React-Router:
When you hit the back button, React Router's history object will look like this:
When you go to any page using history.push record the page you are visiting in the state
Create a decorator, HOC, or whatever type of wrapper you prefer around the React-Router's Route component. In this component: If history.action === "POP" and "history.state.lastVisited === <some page with back navigation disabled>", then you should redirect your user to the /home page using history.replace
Another way to do is is by going to the /home page directly:
Use history.action to detect the back button was used, then:
Change location.href to equal the page you want to visit
Call location.reload(). This will reload the application, and the history will be reset
Browser back button works on your routes history. It will not invoke your programmatically handled routing. That's the point where we should keep maintain history stack with react router. If you are at route '/' and push '/home'. On browser back button it will pop '/home and will go back to '/'.
Even If you implementButton component for go back functionality and use react router history props. Believe me you have to carefully manage your navigation to maintain browser history stack appropriately. So it behaves same like whether you press browser back button or your app Button to go back or go forward.
I hope this would be helpful.
We wanted something similar for our React app and unfortunately this was the best solution we came up with. This is particularly helpful when our users are on mobile devices and they land on a specific page on our site from an ad or a referrer.
This is in our main routes.tsx file.
useEffect(() => {
// The path that the user is supposed to go to
const destinationPath = location.pathname;
// If our site was opened in a new window or tab and accessed directly
// OR the page before this one was NOT a page on our site, then...
if (
document.referrer === window.location.href ||
document.referrer.indexOf(window.location.host) === -1
) {
// Replaces the current pathname with the homepage
history.replace("/");
// Then pushes to the path the user was supposed to go to
history.push(destinationPath);
}
}, []);
Now when a user presses the back button, it takes the user to our homepage instead of being stuck within the "nested" route they were in.
NOTE: There are some small quirks with this implementation. Our app is also a Cordova app so we NEED to have our own back button. This implementation works well with our own back button but does not seem to work with the native browser's back button; hence, it worked well for our needs.

React Router "Link to" does not load new data when called from inside the same component

Background
I am building an app with the following details
react
react-router
redux
it is universal javascript
node js
Problem
When routing with the Link tag from component to component it works perfectly. It calls the data that the component requires and renders the page. But when I click on a Link that uses the same component as the current one all I see is the url change.
Attempts
Things I have tried to get this to work.
Attempt 1
So far I have tried the steps in this question but the solution wont work for me. This was the code I implemented
componentWillReceiveProps(nextProps) {
if (nextProps.article.get('id') !== this.props.article.get('id')) {
console.log('i got trigggerd YESSSSSSSSSSSSSSS');
}
}
But the variable nextProps is always the same as the current props.
Attempt 2
I decided to call the same code I use in componentWillMount but that didn't work either.
componentWillMount() {
let { category, slug } = this.props.params;
this.props.loadArticleState({ category, slug });
}
It just creates an infinite loop when I put this into componentWillReceiveProps.
Conclusion
I belief the problem is clicking the link never calls the data associated with it. Since the data is loaded with
static fetchData({ store, params }) {
let { category, slug } = params;
return store.dispatch(loadArticleState({ category, slug }));
}
Any help is appreciated.
Solution I Used
I created a function to test if the previous data is the same as the changed data.
compareParams(prevProps, props) {
if (!prevProps || typeof prevProps.params !== typeof props.params) {
return false;
}
return Object.is(props.params, prevProps.params);
}
So this tests
are there any previous props?
and then if the props are equal to the previous props?
then return false if there are if this is the case
if not then we see compare props and previous props parameters
In ComponentDidUpdate
In the compoonentDidUpdate we use this function to determine if the data should be updated
componentDidUpdate(prevProps) {
if (this.compareParams(prevProps, this.props)) {
return;
}
this.props[this.constructor.reducerName](this.props.params);
}
Conclusion
This code updates the body of a page that uses the same react component if it receives new data.
maybe you can try use onChange event on Route component, check Route API and then signal to child component that refresh is needed...

Resources