Convert an array to a component in React - javascript

In the project there is an array of objects used for populating the breadcrumb:
export const BREADCRUMBS_LIST = [
{ label: 'Home', path: '/', active: false },
{ label: 'Account', path: '/accounts', active: false },
{ label: 'This Account', path: '/accounts', active: true }
];
it is used to populate the list in the Breadcrumbs component:
import { BREADCRUMBS_LIST } from './...'
...
<Breadcrumbs list={BREADCRUMBS_LIST} />
Everything works fine.
The problem appears when we need to translate those labels based on the user's language. For this, we are using react-intl.
So, I transformed the original array into a component of this form:
import { useIntl } from 'react-intl';
export const BreadcrumbsList = () => {
const intl = useIntl();
return [
{ label: intl.formatMessage({ id: 'Home' }), path: '/', active: false },
{
label: intl.formatMessage({ id: 'Account' }),
path: '/accounts',
active: false
},
{
label: intl.formatMessage({ id: 'This Account' }),
path: '/accounts',
active: true
}
];
};
and use it like this:
<Breadcrumbs list={BreadcrumbsList} />
it seems to be wrong because it returns an error saying:
Cannot read property 'map' of undefined.
In that component, the list was used with map: {list.map(({path, label, active}, index) => {...})
Any ideas how to solve this problem?

Your BreadcrumbsList is actually a custom hook, in order to stick with the Rules of Hooks you need to call it on component's level:
// Add "use" prefix as its a custom hook
const useBreadcrumbsList = () => {
const intl = useIntl();
return [
{ label: intl.formatMessage({ id: "Home" }), path: "/", active: false },
{
label: intl.formatMessage({ id: "Account" }),
path: "/accounts",
active: false,
},
{
label: intl.formatMessage({ id: "This Account" }),
path: "/accounts",
active: true,
},
];
};
// Usage
const Component = () => {
const breadcrumbsList = useBreadcrumbsList();
return <Breadcrumbs list={breadcrumbsList} />;
};

Related

JavaScript: How to update the values of a nested array (linter throwing 'no-param-reassign' warning)

I have an array that looks like this:
const MENUS_LIST: MenuListing[] = [
{
id: 1,
name: 'Analytics',
defaultPath: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview', '/analytics/sales/bookings'],
submenu: [
{
label: 'Overview',
path: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview'],
additionalIcon: [],
name: ['Overview'],
id: 'sales-overview',
},
{
label: 'Bookings',
path: '/analytics/sales/bookings',
relatedPath: ['/analytics/sales/bookings'],
additionalIcon: [],
name: ['Bookings'],
id: 'sales-bookings',
},
],
},
];
I need to convert it to the following format - by adding the isActive flag to the main structure and submenu when the current path === submenu.path.
In the following example, we assume path to be /analytics/sales/overview.
[
{
id: 1,
name: 'Analytics',
defaultPath: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview', '/analytics/sales/bookings'],
isActive: true,
submenu: [
{
label: 'Overview',
path: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview'],
additionalIcon: [],
name: ['Overview'],
id: 'sales-overview',
isActive: true,
},
{
label: 'Bookings',
path: '/analytics/sales/bookings',
relatedPath: ['/analytics/sales/bookings'],
additionalIcon: [],
name: ['Bookings'],
id: 'sales-bookings',
isActive: false,
},
],
},
];
I have the following solution which works (code is simplified):
menuX = (MENUS_LIST as MenuListingProps[]).map((m: MenuListingProps) => {
const resultX = { ...m };
resultX.isActive = true; // will perform checking to determine true or false
(m.submenu as MenuItemProps[]).forEach((sm: MenuItemProps) => {
sm.isActive = true; // linter warning; value assigned based on checking (can be true or false)
sm.icon = 'abc'; // linter warning
sm.title = 'xyz'; // linter warning
});
return resultX;
});
But the linter is complaining of Assignment to property of function parameter "sm" on the lines where I'm assigning values to sm
Based on this SO post, I understand that I need to copy the argument to a temporary variable and work on it instead.
I did this by creating a new var resultX. But I'm not sure how to go about doing the same with sm.
Seeking some guidance, thank you.
menuX = (MENUS_LIST as MenuListingProps[]).map((m: MenuListingProps) => {
const resultX = { ...m };
resultX.isActive = true; // will perform checking to determine true or false
resultX.submenu = (m.submenu as MenuItemProps[]).map((sm: MenuItemProps) => {
const sub = {...sm};
sub.isActive = true; // linter warning; value assigned based on checking (can be true or false)
sub.icon = 'abc'; // linter warning
sub.title = 'xyz'; // linter warning
return sub;
});
return resultX;
});
Here's a method using Object.assign.
Object.assign doesn't mutate the original object, so it returns a new object with the given changes.
const MENUS_LIST = [{"id":1,"name":"Analytics","defaultPath":"/analytics/sales/overview","relatedPath":["/analytics/sales/overview","/analytics/sales/bookings"],"submenu":[{"label":"Overview","path":"/analytics/sales/overview","relatedPath":["/analytics/sales/overview"],"additionalIcon":[],"name":["Overview"],"id":"sales-overview"},{"label":"Bookings","path":"/analytics/sales/bookings","relatedPath":["/analytics/sales/bookings"],"additionalIcon":[],"name":["Bookings"],"id":"sales-bookings"}]}];
const menuX = MENUS_LIST.map(menu => Object.assign(menu, {
isActive: true, // will perform checking to determine true or false
submenu: menu.submenu.map(submenu => Object.assign(submenu, {
isActive: true, // will perform checking to determine true or false
icon: 'abc', // linter warning
title: 'xyz' // linter warning
}))
}));
console.log(menuX);
I hope this code helping you
var array = [
{
id: 1,
name: 'Analytics',
defaultPath: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview', '/analytics/sales/bookings'],
isActive: true,
submenu: [
{
label: 'Overview',
path: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview'],
additionalIcon: [],
name: ['Overview'],
id: 'sales-overview',
isActive: true,
},
{
label: 'Bookings',
path: '/analytics/sales/bookings',
relatedPath: ['/analytics/sales/bookings'],
additionalIcon: [],
name: ['Bookings'],
id: 'sales-bookings',
isActive: false,
},
],
},
];
array.map(o=>o.submenu.map(v=> o.defaultPath == v.path? {...v,isActive:true,icon:"abc",title:'xyz'}:{...v,isActive:false,icon:"abc",title:'xyz'}))

hide array of objects based on route variable

I have below array of route objects having structure below, i am trying to hide menu option Construction and all submenu's under that menu option with the bool variable
const libraryRoutes = ({ isAuth }) => ({
title: 'Library',
icon: <BookOutlined />,
homePath: '/library',
children: [
{
.......
},
{ isDevelopmentMode && { // at here i am getting syntax error
type: 'subMenu',
title: 'Construction',
icon: <ReconciliationOutlined />,
children: [
{
path: '/library/construction-material-type',
exact: true,
title: 'Construction Material Type',
icon: <ReconciliationOutlined />,
},
{
path: '/library/construction-set',
exact: true,
title: 'Construction Set',
icon: <ReconciliationOutlined />,
}
]
}},
{
.......
}
]
});
I am getting syntax error as shown in above code, could any one please let me know how to hide based on routes variable
Try wrapping your ternary condition in (). Else you can build your object outside of the returned value as follow :
const libraryRoutes = ({ isAuth }) => {
const routesForDev = () => {
if (!isDevelopmentMode){
return {};
}
return {
type: 'subMenu',
title: 'Construction',
icon: <ReconciliationOutlined />,
children: [
{
path: '/library/construction-material-type',
exact: true,
title: 'Construction Material Type',
icon: <ReconciliationOutlined />,
},
{
path: '/library/construction-set',
exact: true,
title: 'Construction Set',
icon: <ReconciliationOutlined />,
}
]
}
}
return {
title: 'Library',
icon: <BookOutlined />,
homePath: '/library',
children: [
{
.......
},
routesForDev(),
{
.......
}
]
}
};

trying to send querystring parameters to another page using reactJS

I am trying to send querystring parameters to another page using history object in the button click event like as below
const viewChange = (record) => {
debugger;
history.push('/Viewchange?id=record.id&dataid=record.dataid');
};
and this is route where i have defined in routes.js file
{
path: '/Viewchange/:id/:dataid',
exact: true,
title: 'Viewchange',
icon: 'dashboard',
breadcrumbs: ['dashboard'],
contentComponent: ViewChangeRequest,
isEnabled: () => false,
},
some how i am getting an empty page with url below
http://localhost:3000/Viewchange?id=record.id&dataid=record.dataid#
I am not sure where i am doing wrong in this case, Could any one please let me know how to attach querystring values to url
the below code is the component which i am going to redirect
const ViewChangeRequest = (props) => {
};
export default withRouter(ViewChangeRequest);
many thanks in advance
complete route.js code
[
{
path: '/',
exact: true,
title: 'Dashboard',
icon: 'dashboard',
breadcrumbs: ['Dashboard'],
contentComponent: UserDashboard,
isEnabled: () => true,
},
{
type: 'subMenu',
title: 'Codes and Guidelines',
children: [
{
path: '/LocalCodes',
exact: true,
title: 'Local Codes',
icon: 'dashboard',
breadcrumbs: ['Codes and Guidelines', 'Local Codes'],
contentComponent: AddLocalCode,
isEnabled: () => true,
},
],
},
{
type: 'subMenu',
title: 'Design Criteria',
children: [
{
path: '/Climate',
exact: true,
title: 'Climate',
icon: 'dashboard',
breadcrumbs: ['Design Criteria', 'Climate'],
contentComponent: ContentClimate,
isEnabled: () => true,
},
],
},
{
path: '/Viewchange/:id/:dataid',
title: 'Viewchange',
icon: 'dashboard',
breadcrumbs: ['dashboard'],
contentComponent: ViewChangeRequest,
isEnabled: () => false,
},
];
You need to use backtick character (template literals) to dynamically construct your url.
history.push(`/Viewchange?id=${record.id}&dataid=${record.dataid}`);
There are two ways to construct dynamic Urls:
Using Strings - history.push('/Viewchange?id=' + record.id + '&dataid=' + record.dataid);
Using Bactick -
history.push(`/Viewchange?id=${record.id}&dataid=${record.dataid}`);

How to properly create dictionaries for each record in an api call in JS?

I am trying to create a dictionary for each record returned in an API call.
my broken code:
import lazyLoading from './lazyLoading'
// get records from api call
created() {
axios.get('http://localhost:8080/api/tools/')
.then(response => {
this.json_data = response.data
console.log(this.json_error)
})
.catch(error => {
console.log(error);
})
export default {
name: 'test',
meta: {
icon: 'fa-android',
expanded: false
},
const children = [];
json_data.forEach(item => {
const dict = {
name: item.name,
path: item.path,
meta: {
label: item.label,
link: item.link,
},
component: lazyLoading('testitem/basic'),
}
children.push(dict);
});
}
desired result:
export default {
name: 'test',
meta: {
icon: 'fa-android',
expanded: false
},
children: [
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/basic')
},
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/Basic')
},
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/Basic')
}
]
(obviously 'test' would be replaced what is returned in the api). The main problem is I don't know how to dynamically create the dictionarys. I also have no idea how to view/troubleshoot my axios request. I assumed console.log would spit out the object into the chrome dev tools under console section but I don't see the object there. I'm completely new to javascript so maybe I'm not looking in the correct spot.
Also I'm getting this error:
Module build failed: SyntaxError: 'import' and 'export' may only appear at the top level
So where do I put my api request if I cannot put it at the top?

ES6 get properties from nested objects

i'm doing a project with VueJS, Lodash and Babel, and my problem is that i need to populate a sidebar in my app with routes from a routes.js file. Problem is i don't really know how i can retrieve the data i need.
export default [
{
path: "/admin/login",
name: "admin.login",
component: Login
},
{
path: "/admin",
component: MasterLayout,
meta: { auth: true },
children: [
{
path: "/admin/dashboard",
alias: "/",
menu: { text: "", icon: ""},
name: "admin.dashboard",
component: DashboardIndex
}
]
},
{
path: '/403',
name: 'error.forbidden',
component: ErrorForbidden
},
{
path: "*",
name: 'error.notfound',
component: ErrorPageNotFound
}
];
That is my routes file, i basically need to iterate over each route, check if it has children or a menu property, if it has a menu it gets added to my list of sidebar items, and it's children get added as well with their own menu properties being withdrawn
In the end i'd like to get something like the following
sidebarItems: [
{
name: 'Dashboard',
icon: 'dashboard',
name: 'admin.dashboard'
},
{
name: 'OtherRoute',
icon: 'other-route',
name: 'admin.other-route',
children: [
{
name: 'SubRoute',
icon: 'sub-route',
name: 'admin.sub'
}
]
}
]
This should only account for the routes that contain a menu property.
You basically need iterate recursive over the array, so please try this:
function iterateArrayRoutes(routeArray) {
if(routeArray instanceof Array) {
var routeFormatted = routeArray.reduce(function(last, route){
if (route.hasOwnProperty("menu")) {
var item = {
name: route.name,
icon: route.menu.icon
};
if(route.hasOwnProperty("children") && route.children.length > 0) {
item.children = iterateArrayRoutes(route.children);
}
last.push(item);
}
return last;
}, []);
return routeFormatted;
}
};
Here you have a Demo https://jsfiddle.net/vm1hrwLL/

Categories