Today I was working on my url-shortener React and Node app. I am at the stage where I am building the admin panel, where I can approve Users, see who has signed up, etc. Each of these pages is a component and it has a back button. Since I found myself coding a back button in each of these components, I asked myself how could I write DRYer code - a dynamic back button that would send user a page up in the url path:
For instance, if someone is in /admin/approveusers
back button would bring Users to /admin
. The same button, when placed in Component sitting in /admin/allusers
would bring folks to /admin
.
I started to rummage in the react-router-dom
properties: location
, history
, match
. I found no property that would contain the existing path minus a level. Maybe I missed it or indeed it doesn't exist (I had to try!). There is a goBack()
method in history which was interesting. However, it seemed like a bad idea - even though I expect Users to enter the Component that has the back button by parent path, someone may end up in the Component as a result of typing the direct url. In that case, the back button using goBack()
method in history would create a mess.
I didn't do much research online, and settled with the following. Maybe it is a hack, maybe it is best practice, maybe it's horrible, please let me know in the comments! It works, which is what matters at the end of the day.
I am using the match.path
react-router-dom
property, which contains the path of the component: i.e. /admin/active
. With that, I do the following:
- I split the string by
/
. As a result, I get an array. - I find the last item (the one I want to get rid of).
- I iterate through the array and filter the item I don't want. Using
.filter
. I get another array. - I join the array with '/' and voilà, I get the path one level up i.e. '/admin' instead of '/admin/active'.
See below my component:
import React from 'react';
import { Link } from 'react-router-dom';
const BackButton = ({ match }) => {
const arr = match.path.split('/');
const currPage = arr[arr.length - 1];
const parentPath = arr
.filter((item) => {
return item !== currPage;
})
.join('/');
return <Link to={parentPath}>{`<-- Back`}</Link>;
};
export default BackButton;
EDIT 03/10/2020
What if I use the BackButton
component in the top level of the path (/
)? In that case, there is not one level up to go back to. I refactored my Component to the following:
import React from 'react';
import { Link } from 'react-router-dom';
const BackButton = ({ match, destination }) => {
let parentPath;
if (match.path === '/') {
parentPath = `/${destination}`;
} else {
const arr = match.path.split('/');
const currPage = arr[arr.length - 1];
parentPath = arr
.filter((item) => {
return item !== currPage;
})
.join('/');
}
return (
<Link to={parentPath}>
{match.path === '/'
? `<-- ${destination.charAt(0).toUpperCase() + destination.slice(1)}`
: '<-- Back'}
</Link>
);
};
export default BackButton;
I have added a destination
prop (that I am defining in the parent Component i.e. destination='login'
. I collect that destination and I use it when match.path === '/'
. In that case, I set parentPath
to /${destination}
and the text in the link becomes whatever string coming from the destination
prop with the first letter in capital letter.