Reputation: 916
How can i reduce this logic into much more better one like a reusable hook function, where i can pass the current path and the tabsDetails and it returns me the next path so I can push to the next route once user clicks on next button
const tabsDetails = [
{
path: "/user-details",
},
{
path: "/service-details",
},
{
path: "/previous-experience-details",
},
{
path: "/documents",
},
];
const allowNextRoute = () => {
// early return if tabsDetails is having falsy values or length is 0
if (!tabsDetails?.length) return
const { pathname } = useLocation() //'details/11012/user-details'
const currentPathsSplitted = pathname?.split("/")
const currentPath = currentPathsSplitted.reverse()[0]
const filteredPaths = tabsDetails.map(({ path }) => path)
let currentPathIndex = filteredPaths.indexOf(`/${currentPath}`)
let nextPathIndex = ++currentPathIndex % filteredPaths.length
let nextPath = filteredPaths[nextPathIndex]
currentPathsSplitted.reverse().pop()
currentPathsSplitted.push(nextPath.split("/").reverse()[0])
history.push(currentPathsSplitted.join("/")) // 'details/11012/service-details
}
Upvotes: 2
Views: 124
Reputation: 21120
I would personally start with transforming the structure into one that supports the logic you attempt to deploy.
Instead of using an array you might want to transform the current data in a more usable structure that simplifies path lookup and finding the next item. One such structure could be a "linked list" where you have a "node" with the structure:
{ item: item, next: nextNode }
Then use an object to make lookups a specific paths fast:
const tabDetailsLookup = {
"/user-details": {
item: { path: "/user-details" }
next: -> ref to "/service-details" node
},
"/service-details": {
item: { path: "/service-details" }
next: -> ref to "/previous-experience-details" node
},
// ...
}
You can build this structure using a simple for-loop:
const tabsDetails = [
{ path: "/user-details" },
{ path: "/service-details" },
{ path: "/previous-experience-details" },
{ path: "/documents" },
];
// prepare data structure to fit logic needs
const tabDetailsLookup = (function () {
// place array elements inside nodes
const nodes = tabsDetails.map(item => ({ item }));
const lookup = {};
for (let i = 0; i < nodes.length; ++i) {
// create a reference from the current node to the next node
nodes[i].next = nodes[(i + 1) % nodes.length];
// add the current node to the lookup object
lookup[nodes[i].item.path] = nodes[i];
}
return lookup;
})();
console.log(tabDetailsLookup);
// this structure makes it a lot easier to find the next path
console.log(tabDetailsLookup["/user-details"].next.item.path);
This simplifies:
const filteredPaths = tabsDetails.map(({ path }) => path) let currentPathIndex = filteredPaths.indexOf(`/${currentPath}`) let nextPathIndex = ++currentPathIndex % filteredPaths.length let nextPath = filteredPaths[nextPathIndex]
Into:
const nextPath = tabDetailsLookup[`/${currentPath}`].next.item.path;
You can supplement this with a function that does the other stuff:
function nextPath(path) {
const parts = path.split("/");
if (parts.length == 0) return;
const current = parts.pop();
const node = tabDetailsLookup[`/${current}`];
if (!node) return;
return parts.join("/") + node.next.item.path;
}
const tabsDetails = [
{ path: "/user-details" },
{ path: "/service-details" },
{ path: "/previous-experience-details" },
{ path: "/documents" },
];
// prepare data structure to fit logic needs
const tabDetailsLookup = (function () {
// place array elements inside nodes
const nodes = tabsDetails.map(item => ({ item }));
const lookup = {};
for (let i = 0; i < nodes.length; ++i) {
// create a reference from the current node to the next node
nodes[i].next = nodes[(i + 1) % nodes.length];
// add the current node to the lookup object
lookup[nodes[i].item.path] = nodes[i];
}
return lookup;
})();
function nextPath(path) {
const parts = path.split("/");
if (parts.length == 0) return;
const current = parts.pop();
const node = tabDetailsLookup[`/${current}`];
if (!node) return;
return parts.join("/") + node.next.item.path;
}
console.log(nextPath("details/11012/user-details"));
console.log(nextPath("details/11012/service-details"));
console.log(nextPath("non-existent"));
Upvotes: 1
Reputation: 19319
You can use pop
and findIndex
to reduce the array gymnastics:
const tabsDetails = [{path: "/user-details"},{path: "/service-details"},{path: "/previous-experience-details"},{path: "/documents"}];
const delim = "/";
const allowNextRoute = (journey, resource) => {
if (!journey) return;
const parts = resource.split(delim);
const current = `${delim}${parts.pop()}`;
const path = parts.join(delim);
const currIndex = journey.findIndex(o => o.path === current);
const test = (currIndex < journey.length - 1) && (currIndex > -1);
return test ? `${path}${journey[currIndex + 1].path}` : "";
}
let path;
console.log("Test 1 - OP");
path = "details/11012/user-details"; // useLocation()
while (path) {
console.log(path);
path = allowNextRoute(tabsDetails, path);
}
console.log("Test 2 - no matching path");
path = "details/11012/user-details-error"; // useLocation()
while (path) {
console.log(path);
path = allowNextRoute(tabsDetails, path);
}
console.log("Test 3 - no details");
path = "details/11012/user-details"; // useLocation()
while (path) {
console.log(path);
path = allowNextRoute([], path);
}
console.log("Test 4 - details with no match");
path = "details/11012/user-details"; // useLocation()
while (path) {
console.log(path);
path = allowNextRoute([{path: "/foo"}], path);
}
Upvotes: 1