How to implement react-i18next in a routes.js file - reactjs

I have a left nav bar that utilises my admin.js file. This in turn imports my routes.js file which returns a const with an array. Is it possible to translate these items in any way using react-i18next without breaking the "hooks" rules?
Please note I've implemented react-i18next on my content pages and they work well. Not included any react-i18next imports on below code.
My Admin.js file
import React from "react";
import cx from "classnames";
import { Switch, Route, Redirect } from "react-router-dom";
// creates a beautiful scrollbar
import PerfectScrollbar from "perfect-scrollbar";
import "perfect-scrollbar/css/perfect-scrollbar.css";
// #material-ui/core components
import { makeStyles } from "#material-ui/core/styles";
// core components
import AdminNavbar from "components/Navbars/AdminNavbar.js";
import Footer from "components/Footer/Footer.js";
import Sidebar from "components/Sidebar/Sidebar.js";
import FixedPlugin from "components/FixedPlugin/FixedPlugin.js";
import routes from "routes.js";
import styles from "assets/jss/material-dashboard-pro-react/layouts/adminStyle.js";
var ps;
const useStyles = makeStyles(styles);
export default function Dashboard(props) {
const { ...rest } = props;
// states and functions
const [mobileOpen, setMobileOpen] = React.useState(false);
const [miniActive, setMiniActive] = React.useState(false);
const [image, setImage] = React.useState(require("assets/img/sidebar-2.jpg"));
const [color, setColor] = React.useState("blue");
const [bgColor, setBgColor] = React.useState("black");
// const [hasImage, setHasImage] = React.useState(true);
const [fixedClasses, setFixedClasses] = React.useState("dropdown");
const [logo, setLogo] = React.useState(require("assets/img/logo-white.svg"));
// styles
const classes = useStyles();
const mainPanelClasses =
classes.mainPanel +
" " +
cx({
[classes.mainPanelSidebarMini]: miniActive,
[classes.mainPanelWithPerfectScrollbar]:
navigator.platform.indexOf("Win") > -1
});
// ref for main panel div
const mainPanel = React.createRef();
// effect instead of componentDidMount, componentDidUpdate and componentWillUnmount
React.useEffect(() => {
if (navigator.platform.indexOf("Win") > -1) {
ps = new PerfectScrollbar(mainPanel.current, {
suppressScrollX: true,
suppressScrollY: false
});
document.body.style.overflow = "hidden";
}
window.addEventListener("resize", resizeFunction);
// Specify how to clean up after this effect:
return function cleanup() {
if (navigator.platform.indexOf("Win") > -1) {
ps.destroy();
}
window.removeEventListener("resize", resizeFunction);
};
});
// functions for changeing the states from components
const handleImageClick = image => {
setImage(image);
};
const handleColorClick = color => {
setColor(color);
};
const handleBgColorClick = bgColor => {
switch (bgColor) {
case "white":
setLogo(require("assets/img/logo.svg"));
break;
default:
setLogo(require("assets/img/logo-white.svg"));
break;
}
setBgColor(bgColor);
};
const handleFixedClick = () => {
if (fixedClasses === "dropdown") {
setFixedClasses("dropdown show");
} else {
setFixedClasses("dropdown");
}
};
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const getRoute = () => {
return window.location.pathname !== "/admin/full-screen-maps";
};
const getActiveRoute = routes => {
let activeRoute = "Default Brand Text";
for (let i = 0; i < routes.length; i++) {
if (routes[i].collapse) {
let collapseActiveRoute = getActiveRoute(routes[i].views);
if (collapseActiveRoute !== activeRoute) {
return collapseActiveRoute;
}
} else {
if (
window.location.href.indexOf(routes[i].layout + routes[i].path) !== -1
) {
return routes[i].name;
}
}
}
return activeRoute;
};
const getRoutes = routes => {
return routes.map((prop, key) => {
if (prop.collapse) {
return getRoutes(prop.views);
}
if (prop.layout === "/admin") {
return (
<Route
path={prop.layout + prop.path}
component={prop.component}
key={key}
/>
);
} else {
return null;
}
});
};
const sidebarMinimize = () => {
setMiniActive(!miniActive);
};
const resizeFunction = () => {
if (window.innerWidth >= 960) {
setMobileOpen(false);
}
};
return (
<div className={classes.wrapper}>
<Sidebar
routes={routes}
logoText={"My App"}
logo={logo}
image={image}
handleDrawerToggle={handleDrawerToggle}
open={mobileOpen}
color={color}
bgColor={bgColor}
miniActive={miniActive}
{...rest}
/>
<div className={mainPanelClasses} ref={mainPanel}>
<AdminNavbar
sidebarMinimize={sidebarMinimize.bind(this)}
miniActive={miniActive}
brandText={getActiveRoute(routes)}
handleDrawerToggle={handleDrawerToggle}
{...rest}
/>
{/* On the /maps/full-screen-maps route we want the map to be on full screen - this is not possible if the content and conatiner classes are present because they have some paddings which would make the map smaller */}
{getRoute() ? (
<div className={classes.content}>
<div className={classes.container}>
<Switch>
{getRoutes(routes)}
<Redirect from="/admin" to="/admin/dashboard" />
</Switch>
</div>
</div>
) : (
<div className={classes.map}>
<Switch>
{getRoutes(routes)}
<Redirect from="/admin" to="/admin/dashboard" />
</Switch>
</div>
)}
{getRoute() ? <Footer fluid /> : null}
<FixedPlugin
handleImageClick={handleImageClick}
handleColorClick={handleColorClick}
handleBgColorClick={handleBgColorClick}
color={color}
bgColor={bgColor}
bgImage={image}
handleFixedClick={handleFixedClick}
fixedClasses={fixedClasses}
sidebarMinimize={sidebarMinimize.bind(this)}
miniActive={miniActive}
/>
</div>
</div>
);
}
My routes.js file (wanting to translate the "name" item)
const dashRoutes = [
{
path: "/dashboard",
name: "Dashboard", <--- need to translate this t("Dashboard") and subsequent name attributes below
rtlName: "لوحة القيادة",
icon: DashboardIcon,
component: Dashboard,
layout: "/admin"
},
{
collapse: true,
name: "Pages",
rtlName: "صفحات",
icon: Image,
state: "pageCollapse",
views: [
{
path: "/pricing-page",
name: "Pricing Page",
rtlName: "عالتسعير",
mini: "PP",
rtlMini: "ع",
component: PricingPage,
layout: "/auth"
},
{
path: "/rtl-support-page",
name: "RTL Support",
rtlName: "صودعم رتل",
mini: "RS",
rtlMini: "صو",
component: RTLSupport,
layout: "/rtl"
},
{
path: "/timeline-page",
name: "Timeline Page",
rtlName: "تيالجدول الزمني",
mini: "T",
rtlMini: "تي",
component: TimelinePage,
layout: "/admin"
},
{
path: "/login-page",
name: "Login Page",
rtlName: "هعذاتسجيل الدخول",
mini: "L",
rtlMini: "هعذا",
component: LoginPage,
layout: "/auth"
},
{
path: "/register-page",
name: "Register Page",
rtlName: "تسجيل",
mini: "R",
rtlMini: "صع",
component: RegisterPage,
layout: "/auth"
},
{
path: "/lock-screen-page",
name: "Lock Screen Page",
rtlName: "اقفل الشاشة",
mini: "LS",
rtlMini: "هذاع",
component: LockScreenPage,
layout: "/auth"
},
{
path: "/user-page",
name: "User Profile",
rtlName: "ملف تعريفي للمستخدم",
mini: "UP",
rtlMini: "شع",
component: UserProfile,
layout: "/admin"
},
{
path: "/error-page",
name: "Error Page",
rtlName: "صفحة الخطأ",
mini: "E",
rtlMini: "البريد",
component: ErrorPage,
layout: "/auth"
}
]
},
{
collapse: true,
name: "Components",
rtlName: "المكونات",
icon: Apps,
state: "componentsCollapse",
views: [
{
collapse: true,
name: "Multi Level Collapse",
rtlName: "انهيار متعدد المستويات",
mini: "MC",
rtlMini: "ر",
state: "multiCollapse",
views: [
{
path: "/buttons",
name: "Buttons",
rtlName: "وصفت",
mini: "B",
rtlMini: "ب",
component: Buttons,
layout: "/admin"
}
]
},
{
path: "/buttons",
name: "Buttons",
rtlName: "وصفت",
mini: "B",
rtlMini: "ب",
component: Buttons,
layout: "/admin"
},
{
path: "/grid-system",
name: "Grid System",
rtlName: "نظام الشبكة",
mini: "GS",
rtlMini: "زو",
component: GridSystem,
layout: "/admin"
},
{
path: "/panels",
name: "Panels",
rtlName: "لوحات",
mini: "P",
rtlMini: "ع",
component: Panels,
layout: "/admin"
},
{
path: "/sweet-alert",
name: "Sweet Alert",
rtlName: "الحلو تنبيه",
mini: "SA",
rtlMini: "ومن",
component: SweetAlert,
layout: "/admin"
},
{
path: "/notifications",
name: "Notifications",
rtlName: "إخطارات",
mini: "N",
rtlMini: "ن",
component: Notifications,
layout: "/admin"
},
{
path: "/icons",
name: "Icons",
rtlName: "الرموز",
mini: "I",
rtlMini: "و",
component: Icons,
layout: "/admin"
},
{
path: "/typography",
name: "Typography",
rtlName: "طباعة",
mini: "T",
rtlMini: "ر",
component: Typography,
layout: "/admin"
}
]
},
{
collapse: true,
name: "Forms",
rtlName: "إستمارات",
icon: "content_paste",
state: "formsCollapse",
views: [
{
path: "/regular-forms",
name: "Regular Forms",
rtlName: "أشكال عادية",
mini: "RF",
rtlMini: "صو",
component: RegularForms,
layout: "/admin"
},
{
path: "/extended-forms",
name: "Extended Forms",
rtlName: "نماذج موسعة",
mini: "EF",
rtlMini: "هوو",
component: ExtendedForms,
layout: "/admin"
},
{
path: "/validation-forms",
name: "Validation Forms",
rtlName: "نماذج التحقق من الصحة",
mini: "VF",
rtlMini: "تو",
component: ValidationForms,
layout: "/admin"
},
{
path: "/wizard",
name: "Wizard",
rtlName: "ساحر",
mini: "W",
rtlMini: "ث",
component: Wizard,
layout: "/admin"
}
]
},
{
collapse: true,
name: "Tables",
rtlName: "الجداول",
icon: GridOn,
state: "tablesCollapse",
views: [
{
path: "/regular-tables",
name: "Regular Tables",
rtlName: "طاولات عادية",
mini: "RT",
rtlMini: "صر",
component: RegularTables,
layout: "/admin"
},
{
path: "/extended-tables",
name: "Extended Tables",
rtlName: "جداول ممتدة",
mini: "ET",
rtlMini: "هور",
component: ExtendedTables,
layout: "/admin"
},
{
path: "/react-tables",
name: "React Tables",
rtlName: "رد فعل الطاولة",
mini: "RT",
rtlMini: "در",
component: ReactTables,
layout: "/admin"
}
]
},
{
collapse: true,
name: "Maps",
rtlName: "خرائط",
icon: Place,
state: "mapsCollapse",
views: [
{
path: "/google-maps",
name: "Google Maps",
rtlName: "خرائط جوجل",
mini: "GM",
rtlMini: "زم",
component: GoogleMaps,
layout: "/admin"
},
{
path: "/full-screen-maps",
name: "Full Screen Map",
rtlName: "خريطة كاملة الشاشة",
mini: "FSM",
rtlMini: "ووم",
component: FullScreenMap,
layout: "/admin"
},
{
path: "/vector-maps",
name: "Vector Map",
rtlName: "خريطة المتجه",
mini: "VM",
rtlMini: "تم",
component: VectorMap,
layout: "/admin"
}
]
},
{
path: "/widgets",
name: "Widgets",
rtlName: "الحاجيات",
icon: WidgetsIcon,
component: Widgets,
layout: "/admin"
},
{
path: "/charts",
name: "Charts",
rtlName: "الرسوم البيانية",
icon: Timeline,
component: Charts,
layout: "/admin"
},
{
path: "/calendar",
name: "Calendar",
rtlName: "التقويم",
icon: DateRange,
component: Calendar,
layout: "/admin"
}
];
export default dashRoutes;

you can do something like this.
import { withTranslation } from "react-i18next";
import i18n from "../../../../i18n" // import here your i18n file
const dashRoutes = [
{
path: "/dashboard",
name: i18n("Dashboard"), <--- need to translate this t("Dashboard") and subsequent name attributes below
rtlName: "لوحة القيادة",
icon: DashboardIcon,
component: Dashboard,
layout: "/admin"
},
{
collapse: true,
name: i18n("Page"),
rtlName: "صفحات",
icon: Image,
state: "pageCollapse",
......
export default withTranslation()(dashRoutes);
or you can use useTranslation
import { useTranslation } from "react-i18next";
const {t} = useTranslation();
const dashRoutes = [
{
path: "/dashboard",
name: t("Dashboard"), <--- need to translate this t("Dashboard") and subsequent name attributes below
rtlName: "لوحة القيادة",
icon: DashboardIcon,
component: Dashboard,
layout: "/admin"
},
...
i hope it can help you to translate your array name .

Related

With react DnD, why my state resets when every time I drop an element?

I am working on a form builder. The basic implementation works but every time when I move elements from the tool box to the form the current state resets to initial then add newly dropped element so previously dragged element is missed from the form. Below is the linked to the source code at sandbox.
https://codesandbox.io/s/nifty-https-qx8lko
index.js
import Example from "./Example.jsx";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import "./style.sass";
function App() {
return (
<div className="App">
<DndProvider backend={HTML5Backend}>
<Example />
</DndProvider>
</div>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
Example.jsx
import React, { useState } from "react";
import Form from "./Form";
import Toolbar from "./Toolbar";
const Example = (props) => {
const [Items, setItems] = useState([{ name: "Test" }]);
const addItem = (item) => {
setItems([...Items, item]);
};
return (
<div className="card" style={{ border: "1px dashed rgb(219 163 163)" }}>
<div className="card-header d-flex justify-content-between">
<h5 className="cart-title m-0">View</h5>
</div>
<div className="card-body">
<div className="row">
<div className="col-9">
<Form items={Items} addItems={addItem} />
</div>
<div className="col-3">
<Toolbar />
</div>
</div>
</div>
</div>
);
};
export default Example;
Form.jsx
import React from "react";
import { useDrop } from "react-dnd";
import { ItemTypes } from "./ItemTypes";
function Form(props) {
const addItem = (item) => {
props.addItems(item);
};
const [{ canDrop, isOver }, drop] = useDrop(() => ({
accept: ItemTypes.CARD,
drop: (item, monitor) => addItem(item),
collect: (monitor) => ({
isOver: monitor.isOver(),
canDrop: monitor.canDrop()
})
}));
return (
<div className="card card-default shadow-sm mt-3">
<div className="card-body">
{props.items.map((item, index) => {
return (
<div key={index} className="form-item">
{item.name}
</div>
);
})}
<div ref={drop}>
{/* {canDrop ? "Release to drop" : "Drag a box here"} */}
{isOver && canDrop && (
<div className="form-place-holder">
<div>Release to Drop</div>
</div>
)}
{!isOver && (
<div className="form-place-holder">
<div>Dropzone</div>
</div>
)}
</div>
</div>
</div>
);
}
export default Form;
Toolbar.jsx
import React from "react";
import ToolbarItem from "./ToolbarItem";
const Toolbar = () => {
const _defaultItems = () => {
return [
{
key: "Header",
name: "Header Text",
icon: "fas fa-heading",
static: true,
content: "Place holder text"
},
{
key: "Label",
name: "Label",
static: true,
icon: "fas fa-font",
content: "Place holder text"
},
{
key: "Paragraph",
name: "Paragraph",
static: true,
icon: "fas fa-paragraph",
content: "Place holder text"
},
{
key: "LineBreak",
name: "Line break",
static: true,
icon: "fas fa-arrows-alt-h"
},
{
key: "Dropdown",
canHaveAnswer: true,
name: "Dropdown",
icon: "far fa-caret-square-down",
label: "Place holder label",
field_name: "dropdown_",
options: []
},
{
key: "Tags",
canHaveAnswer: true,
name: "Tags",
icon: "fas fa-tags",
label: "Place holder label",
field_name: "tags_",
options: []
},
{
key: "Checkboxes",
canHaveAnswer: true,
name: "Checkboxes",
icon: "far fa-check-square",
label: "Place holder label",
field_name: "checkboxes_",
options: []
},
{
key: "RadioButtons",
canHaveAnswer: true,
name: "Multiple choices",
icon: "far fa-dot-circle",
label: "Place holder label",
field_name: "radiobuttons_",
options: []
},
{
key: "TextInput",
canHaveAnswer: true,
name: "Text input",
label: "Place holder label",
icon: "fas fa-font",
field_name: "text_input_"
},
{
key: "NumberInput",
canHaveAnswer: true,
name: "Number input",
label: "Place holder label",
icon: "fas fa-plus",
field_name: "number_input_"
},
{
key: "TextArea",
canHaveAnswer: true,
name: "Multi line input",
label: "Place holder label",
icon: "fas fa-text-height",
field_name: "text_area_"
},
{
key: "TwoColumnRow",
canHaveAnswer: false,
name: "Two columns row",
label: "",
icon: "fas fa-columns",
field_name: "two_col_row_"
},
{
key: "ThreeColumnRow",
canHaveAnswer: false,
name: "Three columns row",
label: "",
icon: "fas fa-columns",
field_name: "three_col_row_"
},
{
key: "FourColumnRow",
canHaveAnswer: false,
name: "Four columns row",
label: "",
icon: "fas fa-columns",
field_name: "four_col_row_"
},
{
key: "Image",
name: "Image",
label: "",
icon: "far fa-image",
field_name: "image_",
src: ""
},
{
key: "Rating",
canHaveAnswer: true,
name: "Rating",
label: "Place holder label",
icon: "fas fa-star",
field_name: "rating_"
},
{
key: "DatePicker",
canDefaultToday: true,
canReadOnly: true,
dateFormat: "MM/dd/yyyy",
timeFormat: "hh:mm aa",
showTimeSelect: false,
showTimeSelectOnly: false,
showTimeInput: false,
name: "Date",
icon: "far fa-calendar-alt",
label: "Place holder label",
field_name: "date_picker_"
},
{
key: "Signature",
canReadOnly: true,
name: "Signature",
icon: "fas fa-pen-square",
label: "Signature",
field_name: "signature_"
},
{
key: "HyperLink",
name: "Website",
icon: "fas fa-link",
static: true,
content: "Place holder website link",
href: "http://www.example.com"
},
{
key: "Download",
name: "File attachment",
icon: "fas fa-file",
static: true,
content: "Place holder file name",
field_name: "download_",
file_path: "",
_href: ""
},
{
key: "Range",
name: "Range",
icon: "fas fa-sliders-h",
label: "Place holder label",
field_name: "range_",
step: 1,
default_value: 3,
min_value: 1,
max_value: 5,
min_label: "Easy",
max_label: "Difficult"
},
{
key: "Camera",
name: "Camera",
icon: "fas fa-camera",
label: "Place holder label",
field_name: "camera_"
},
{
key: "FileUpload",
name: "File upload",
icon: "fas fa-file",
label: "Place holder label",
field_name: "file_upload_"
}
];
};
return (
<div className="react-form-builder-toolbar">
<h4>Toolbox</h4>
<ul>
{_defaultItems().map((item, index) => {
return <ToolbarItem data={item} key={index} />;
})}
</ul>
</div>
);
};
export default Toolbar;
ToolbarItem.jsx
import React from "react";
import { useDrag } from "react-dnd";
import { ItemTypes } from "./ItemTypes";
function ToolbarItem(props) {
const data = props.data;
const [{ isDragging }, drag, dragPreview] = useDrag(() => ({
type: ItemTypes.CARD,
collect: (monitor) => ({
isDragging: monitor.isDragging()
}),
item: props.data
}));
return (
<li ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }}>
<i className={data.icon}></i>
{data.name}
</li>
);
}
export default ToolbarItem;
I have tried also storing the state in redux and still same behavior. For simplicity I have kept the state in parent component in Sandbox example.
In Example.js file try replacing with this setItems((items)=>[...items, item]);
I think this will fix the issue. Check this here https://codesandbox.io/s/reactjs-dnd-example-forked-1lu5i9?file=/src/Form.jsx

Rendering Material-UI icons from an array

Background info:
I'm using react and material-ui.
To keep the code clean, I populate menu items from a const array, like so:
const menuItems = [
{ label: "Home", path: "/home" },
{ label: "Accounts", path: "/accounts" },
{ label: "Organizations", path: "/organizations" },
];
Each item in the array is an object containing a label and a redirect path. I map over the items when rendering. Very basic.
Problem:
I would like to include a material-ui icon component in the menuItems array so the icon can be rendered next to the label. But I can't find a way to reference the icons by a name string
https://material-ui.com/components/material-icons/
Possible solutions:
put the icon component into a string:
{ label: "Accounts", path: "/accounts" }, icon: "<AccountBox/>"} but then I somehow need to evaluate the string into jsx. I don't know how.
Make a react functional component which renders a different icon depending on a prop, for example: <IconSwitch icon = {"accountIcon"} /> and hard-code different icons inside the RFC. Not pretty, but should work.
Punt and use different icons such as svg icons or font icons that can referenced by a name string.
Any suggestions on how to do this?
Thanks
Icon Font
You can use the Icon component. https://material-ui.com/components/icons/#icon-font-icons
To use an icon simply wrap the icon name (font ligature) with the Icon component, for example:
import Icon from '#material-ui/core/Icon';
<Icon>star</Icon>
https://codesandbox.io/s/material-demo-forked-sj66h?file=/demo.tsx
Assuming you set up your menu items with the appropriate icon ligatures:
const menuItems = [
{ label: "Home", path: "/home", icon: "home" },
{ label: "Accounts", path: "/accounts", icon: "account_circle" },
{ label: "Organizations", path: "/organizations", icon: "settings" }
];
Then you can map over them:
{menuItems.map(({ label, icon }) => {
return (
<span key={label}>
{label} <Icon>{icon}</Icon>
</span>
);
})}
SVG Icons
If you want to use SVG icons instead of basic icons, I'd recommend pulling only the SVG icons you plan to use in order to allow the icons you aren't using to be tree-shaken from the resulting bundle. The ability to tree shake is a good reason to use SVG icons over font icons.
import { Home, AccountCircle, Settings, AddCircle } from "#material-ui/icons";
If you want to allow user input of all icons or aren't aware ahead of time which icons will be displayed, you can import everything from #material-ui/icons as in Jonathan's answer.
If you aren't putting the list of icons into something that needs to be able to be stringified (i.e. Redux/sent through an API call) then you can just directly put the icons into the array and render them:
const menuItems: MenuItem[] = [
{ label: "Home", path: "/home", icon: <Home /> },
{ label: "Accounts", path: "/accounts", icon: <AccountCircle /> },
{ label: "Organizations", path: "/organizations", icon: <Settings /> }
];
// Rendering:
{menuItems.map(({ label, icon }) => {
return (
<span key={label}>
{label} {icon}
</span>
);
})}
If you are going to put the Icons somewhere that needs to be stringified, the above won't work, so I'd recommend putting the icons you want to use into an object to map them. That way you have a string to icon map.
Example: https://codesandbox.io/s/material-icons-svg-udcv3?file=/demo.tsx
import { Home, AccountCircle, Settings, AddCircle } from "#material-ui/icons";
const icons = {
Home,
AccountCircle,
Settings
};
In the case of the example above (i.e. rendering the icons from an array)
interface MenuItem {
label: string;
path: string;
icon: keyof typeof icons;
}
const menuItems: MenuItem[] = [
{ label: "Home", path: "/home", icon: "Home" },
{ label: "Accounts", path: "/accounts", icon: "AccountCircle" },
{ label: "Organizations", path: "/organizations", icon: "Settings" }
];
// Rendering:
{menuItems.map(({ label, icon }) => {
const Icon = icons[icon];
return (
<span key={label}>
{label} <Icon />
</span>
);
})}
You can import all from #material-ui/icons and than create an Icon component dynamically:
import React from 'react'
import * as icons from '#material-ui/icons'
interface MenuItem {
label: string,
icon: keyof typeof icons,
path: string
}
export function Menu() {
const menuItems: MenuItem[] = [
{ label: 'Home', path: './home', icon: 'Home' },
{ label: 'Accounts', path: './accounts', icon: 'AccountCircle' },
{ label: 'Organizations', path: './organizations', icon: 'Settings' }
]
return (
<>
{menuItems.map(menuItem => {
const Icon = icons[menuItem.icon]
return (
<span key={menuItem.path}>
{menuItem.label} <Icon />
</span>
)
})}
</>
)
}
// I have better way to avoid all of this other hustle .
// 1: Make Every icon in Array which is in Jsx from to simple name.
// Ex:
[
{ Name: "New", Icon: <HomeIcon /> },
{ Name: "JS Mastery", Icon: <CodeIcon /> },
{ Name: "Coding", Icon: <CodeIcon /> },
{ Name: "ReactJS", Icon: <CodeIcon /> },
{ Name: "NextJS", Icon: <CodeIcon /> },
]
to
[
({ Name: "New", Icon: HomeIcon },
{ Name: "JS Mastery", Icon: CodeIcon },
{ Name: "Coding", Icon: CodeIcon },
{ Name: "ReactJS", Icon: CodeIcon })
];
// 2: Remember Using Object keys name as capital ,here:- "Name , Icon" not "name , icon".
// 3: Now Simply use : -
{
categories.map(({ Name, Icon }) => (
<button key={Name}>
<span>{Name}</span>
<span> {<Icon/>} </span>
</button>
));
}
//use icon in this cleaver way

how to use icon in Ant-design/icons with V4

I have tried to make a menu, so i create a menuList to config the menu with getMenuNodes(), but Ant framework has been upgraded from v3 to v4 which the icon method has been changed. they are now using icon={<PieChartOutlined />} to instead of icon='PieChartOutlined', everything is working well, the icon area shows the word <PieChartOutlined />right now. i do not know why it happened, please help me to solve this problem.
left-navigation.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import logo from '../../assets/images/logo.png';
import './index.less';
import { Menu } from 'antd';
import { PieChartOutlined } from '#ant-design/icons';
import menuList from '../../config/menuConfig';
const { SubMenu } = Menu;
export default class LeftNav extends Component {
getMenuNodes = menuList => {
return menuList.map(item => {
if (!item.children) {
return (
<Menu.Item key={item.key} icon={item.icon}>
<Link to={item.key}>{item.title}</Link>
</Menu.Item>
);
} else {
return (
<SubMenu key={item.key} icon={item.icon} title={item.title}>
{this.getMenuNodes(item.children)}
</SubMenu>
);
}
});
};
render() {
return (
<div className="left-nav">
<Link to="./" className="left-nav-header">
<img src={logo} alt="" />
<h1>Backend System</h1>
</Link>
<Menu
mode="inline"
theme="dark"
>
{this.getMenuNodes(menuList)}
</Menu>
</div>
);
}
}
menuList.js
const menuList = [
{
title: 'Home',
key: '/home',
icon: '<PieChartOutlined />',
},
{
title: 'Item',
key: '/products',
icon: '<PieChartOutlined />',
children: [
{
title: 'Category Control',
key: '/category',
icon: '<PieChartOutlined />',
},
{
title: 'Product Control',
key: '/product',
icon: '<PieChartOutlined />',
},
],
},
{
title: 'User Control',
key: '/user',
icon: '<PieChartOutlined />',
},
{
title: 'Role Control',
key: '/role',
icon: '<PieChartOutlined />',
},
{
title: 'Diagram',
key: '/charts',
icon: '<PieChartOutlined />',
children: [
{
title: 'Bar',
key: '/charts/bar',
icon: '<PieChartOutlined />',
},
{
title: 'Line',
key: '/charts/line',
icon: '<PieChartOutlined />',
},
{
title: 'Pie',
key: '/charts/pie',
icon: '<PieChartOutlined />',
},
],
},
];
export default menuList;
You are passing a string of '<PieChartOutlined />', you need to pass the component directly.
import { PieChartOutlined } from '#ant-design/icons';
and:
{
title: 'Product Control',
key: '/product',
icon: <PieChartOutlined />,
},
You'll need to install ant-design/icons if you haven't already:
npm install --save #ant-design/icons
Since reason performance on the previous version, antd team was apply tree-shaking to use icon. More detail, you can check https://ant.design/docs/react/migration-v4

How do I render this object in React with React Router?

How do I render this object using react router? I want to render this object in a menu.
{main: 'Main', about: 'About', contacts: 'Contacts', '404': 404}
Refactor it this way:
const menu = [
{ title: 'Main', path: '/main' },
{ title: 'About', path: '/about' },
{ title: 'Contacts', path: '/contacts' },
{ title: '404', path: '/404' }
];
Now do this in your JSX wherever you want your menu:
{menu.map(item => <Link to={item.path} alt={item.title} />)}
Don't forget to import Link:
import { Link } from 'react-router-dom'

How to show selected item in list with a different color in react native using native base ui kit?

I want to show the sidebar content in a different background color.For that I've tried TouchableOpacity underlay.But that is not the one I'm looking for.After giving TouchableOpacity ,it will change the color of the text only not the entire list background.How do I change the listitem background color as I'm using native base ui kit.Please help.Is there any method to do that?This is how the sidebar looks like.I've done something likes the following.Setting pressStatus as true within onPresList and if it is true change backround color.But navigation to route is not working.There is a mistake
https://i.stack.imgur.com/w9YiR.png
How do I change background color onPress? Following is my code.
updated
import React, { Component } from "react";
import { Image, FlatList } from "react-native";
import {
Content,
Text,
List,
ListItem,
Icon,
Container,
Left,
Right,
Badge,
Thumbnail
} from "native-base";
import styles from "./style";
const drawerCover = require("../../imgs/quwait.jpg");
const datas = [
{
name: "Dashboard",
route: "Anatomy",
icon: require("../../imgs/dashboard.png"),
},
{
name: "Companies",
route: "Header",
icon: require("../../imgs/enterprise1.png"),
},
{
name: "Company Admin",
route: "Footer",
icon: require("../../imgs/icon1.png"),
},
{
name: "Employee",
route: "NHBadge",
icon: require("../../imgs/businessman1.png"),
},
{
name: "Employs",
route: "NHButton",
icon: require("../../imgs/employee1.png"),
},
{
name: "Announcement",
route: "NHCard",
icon: require("../../imgs/megaphone1.png"),
},
{
name: "Holiday",
route: "Check",
icon: require("../../imgs/sun-umbrella1.png"),
},
{
name: "Accounting Report",
route: "NHTypography",
icon: require("../../imgs/accounting1.png"),
},
{
name: "Invoice",
route: "NHCheckbox",
icon: require('../../imgs/approve-invoice1.png'),
},
{
name: "Settings",
route: "NHDatePicker",
icon: require('../../imgs/settings1.png'),
},
{
name: "Safety Phone Numbers",
route: "NHThumbnail",
icon: "user",
},
{
name: "NBK",
route: "NHDeckSwiper",
icon: "swap",
},
{
name: "ABK",
route: "NHFab",
icon: "help-buoy",
},
{
name: "CBK",
route: "NHForm",
icon: "call",
},
{
name: "Daily Invoice",
route: "NHIcon",
icon: "information-circle",
},
{
name: "Kolin",
route: "NHLayout",
icon: "grid",
},
{
name: "Limak",
route: "NHList",
icon: "lock",
},
{
name: "Polaytol",
route: "ListSwipe",
icon: "code-working",
},
{
name: "ACTS",
route: "NHPicker",
icon: "arrow-dropdown",
}
];
class SideBar extends Component {
constructor(props) {
super(props);
this.state = {
shadowOffsetWidth: 1,
shadowRadius: 4,
pressStatus:false
};
}
onPressList = (DATA, INDEX) => {
this.props.navigation.navigate(DATA.route);
this.setState({ pressStatus : true, selectedItem: INDEX});
}
render() {
return (
<Container>
<Content
bounces={false}
style={{ flex: 1, backgroundColor: "#fff", top: -1 }}
>
<Image source={drawerCover} style={styles.drawerCover} />
<FlatList
data={datas}
keyExtractor={(item, index) => String(index)}
renderItem={({ DATA, INDEX }) => {
<ListItem
button
noBorder
onPress={() => this.onPressList(DATA, INDEX)}
style={{
backgroundColor:
this.state.selectedItem === INDEX ? "#cde1f9" : "transparent"
}}
>
<Left>
<Image
source={DATA.icon }
style={{width:30,height:30}}
/>
<Text style={styles.text}>
{DATA.name}
</Text>
</Left>
</ListItem>}}
/>
</Content>
</Container>
);
}
}
export default SideBar;
In the App example from native Base they don't support styles for background items list. So you should change your List component from NativeBase and add a FlatList Component from react native. But you should also return the ListItem component from NativeBase and don't forget the import { FlatList } from "react-native";
You should also modify the onPressList function (I would transform it into an arrow function)
In your states you need to add the state selectedItem: 0
Everytime you press an item, your function would be called by modifying a selectedItem idex, which tells the Flatlist, which Item should get which background. I think this has to be the solution.
If it doesn't compile, make sure that you support arrow functions and that any curly braces or something like that isn't missing.
Final Code UPDATE
import React, { Component } from "react";
import { Image, FlatList } from "react-native";
import {
Content,
Text,
List,
ListItem,
Icon,
Container,
Left,
Right,
Badge,
Thumbnail
} from "native-base";
import styles from "./style";
const drawerCover = require("../../imgs/quwait.jpg");
const datas = [
{
name: "Dashboard",
route: "Anatomy",
icon: require("../../imgs/dashboard.png"),
},
{
name: "Companies",
route: "Header",
icon: require("../../imgs/enterprise1.png"),
},
{
name: "Company Admin",
route: "Footer",
icon: require("../../imgs/icon1.png"),
},
{
name: "Employee",
route: "NHBadge",
icon: require("../../imgs/businessman1.png"),
},
{
name: "Employs",
route: "NHButton",
icon: require("../../imgs/employee1.png"),
},
{
name: "Announcement",
route: "NHCard",
icon: require("../../imgs/megaphone1.png"),
},
{
name: "Holiday",
route: "Check",
icon: require("../../imgs/sun-umbrella1.png"),
},
{
name: "Accounting Report",
route: "NHTypography",
icon: require("../../imgs/accounting1.png"),
},
{
name: "Invoice",
route: "NHCheckbox",
icon: require('../../imgs/approve-invoice1.png'),
},
{
name: "Settings",
route: "NHDatePicker",
icon: require('../../imgs/settings1.png'),
},
{
name: "Safety Phone Numbers",
route: "NHThumbnail",
icon: "user",
},
{
name: "NBK",
route: "NHDeckSwiper",
icon: "swap",
},
{
name: "ABK",
route: "NHFab",
icon: "help-buoy",
},
{
name: "CBK",
route: "NHForm",
icon: "call",
},
{
name: "Daily Invoice",
route: "NHIcon",
icon: "information-circle",
},
{
name: "Kolin",
route: "NHLayout",
icon: "grid",
},
{
name: "Limak",
route: "NHList",
icon: "lock",
},
{
name: "Polaytol",
route: "ListSwipe",
icon: "code-working",
},
{
name: "ACTS",
route: "NHPicker",
icon: "arrow-dropdown",
}
];
class SideBar extends Component {
constructor(props) {
super(props);
this.state = {
shadowOffsetWidth: 1,
shadowRadius: 4,
pressStatus:false,
selectedItem:0
};
}
onPressList = (data, index) => {
this.props.navigation.navigate(data.route);
this.setState({ pressStatus : true, selectedItem: index});
}
render() {
return (
<Container>
<Content
bounces={false}
style={{ flex: 1, backgroundColor: "#fff", top: -1 }}
>
<Image source={drawerCover} style={styles.drawerCover} />
<FlatList
data={datas}
keyExtractor={(item, index) => String(index)}
extraData={this.state.selectedItem}
renderItem={({item:data, index}) => {
const { selectedItem: sd } = this.state
const localColor ={backgroundColor: sd === index ? "#cde1f9" : "transparent"}
return (
<ListItem
button
noBorder
style={localColor}
onPress={() => this.onPressList(data, index)}
>
<Left>
<Image
source={data.icon}
style={{width:30,height:30}}
/>
<Text style={styles.text}>
{data.name}
</Text>
</Left>
</ListItem>
)
}}
/>
</Content>
</Container>
);
}
}
export default SideBar;

Resources