How to change menu on button click? - javascript

I am trying to make an authentication based menus in react app.
Menu Data:
const menuItems = {
primaryMenus: [
{ title: 'Messages' },
{ title: 'Register' },
{
subMenus: {
title: 'Account',
menus: [{ title: "Profile" }, { title: "Change Password"}],
},
},
{ title: 'Help' }
],
};
From the above data, I need to build up the menu structure.
The code that I have tried so far
const menuItems = {
primaryMenus: [
{ title: 'Messages' },
{ title: 'Register' },
{
subMenus: {
title: 'Account',
menus: [{ title: "Profile" }, { title: "Change Password"}],
},
},
{ title: 'Help' }
],
};
function App() {
const [ isAuthenticated, setAuthenticated ] = React.useState(false);
return(
<div>
<button onClick={() => {setAuthenticated(!isAuthenticated)}}> {isAuthenticated ? 'Logout' : 'Login'} </button>
<ul className="menu">
{menuItems.primaryMenus.map((menu, i) => {
return (
!menu.subMenus ?
<li key={i}> {menu.title} </li>
:
<li key={i}>
{menu.subMenus.title}
<ul>
{ menu.subMenus.menus.map((submenu, j) => {
return <li key={j}> {submenu.title} </li>
}) }
</ul>
</li>
)
})}
</ul>
<h1> The menu Messages and Help will be there for both logged in user and logged out user </h1>
<br />
<h1> Whereas the Register menu will be available only if user is logged out </h1>
<br />
<h1> My Account menu and its submenus will be available only if user is logged in </h1>
</div>
)
}
ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/react#16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
Requirement:
Menu structure for logged in user:
- Messages
- Account
- Profile
- Change Password
- Help
Menu structure for logged out user:
- Messages
- Register
- Help
I can modify the provided json structure (menuItems) as well..
Kindly help me to achieve the above result.
I am new to react, so if anyone could provide me a solution in pure react way of authentication then it would be much more helpful for me..

I would recommend you changed the structure of the array menuItems. For example like this:
const menuItems = [
{ title: 'Messages', whenLoggedIn: true, whenLoggedOut: true },
{ title: 'Help', whenLoggedIn: true, whenLoggedOut: true },
{ title: 'Register', whenLoggedIn: false, whenLoggedOut: true },
{
title: 'Account',
whenLoggedIn: true,
whenLoggedOut: false,
subMenuItems: [{ title: 'Profile' }, { title: 'Change password' }],
},
];
Then you can use them like this:
const keepMenuItem = (menuItem, isAuthenticated) =>
(isAuthenticated && menuItem.whenLoggedIn)
|| (!isAuthenticated && menuItem.whenLoggedOut)
return (
<ul className="menu">
{menuItems.filter(menuItem => keepMenuItem(menuItem, isAuthenticated)
.map(menuItem => (
<li key={menuItem.title}>
{menuItem.title}
{!!menuItem.subMenuItems && (
<ul>
{menuItem.subMenuItems.map(subMenuItem => (
<li key={subMenuItem.title}>{subMenuItem.title}</li>
))}{' '}
</ul>
)}
</li>
))}
</ul>
);

Related

How to access outer variable inside of Array[..]?

I am working with React and trying to build a multi-level and dynamic navmenu with submenu and sidemenu.
This is my nav.js component.
import React from "react";
import SubMenu from "./submenu";
function Navbar () {
return (
<>
<ul>
<li>
<span> Home </span>
</li>
<li>
<span> Category </span>
<SubMenu subtitle="Category" />
</li>
<li>
<span> Pages </span>
<SubMenu subtitle="Pages" />
</li>
</ul>
</>
);
}
export default Navbar;
And this is my submenu.js component.
import React from "react";
function SubMenu (props) {
const navtitle = props.subtitle;
const allSubMenuItem = {
Category: [
{
id: 1,
title: "Grocery",
},
{
id: 2,
title: "Fashion",
},
{
id: 3,
title: "Electronics",
}
]
Pages: [
{
id: 1,
title: "Abouts Us",
},
{
id: 2,
title: "Contact us",
},
{
id: 3,
title: "Term & Conditions",
}
]
}
function menuBody (index, title) {
const sid = index;
const stitle = title;
return (
<>
<li key={ sid }>
<span> { stitle } </span>
</li>
</>
);
};
return (
<>
<ul>
{
allSubMenuItem[`"${navtitle}"`].map((data, index) => (
menuBody(index, data.title)
))
}
</ul>
</>
);
}
export default SubMenu;
I am getting an error "Uncaught TypeError: allSubMenuItem[((""" + (intermediate value)) + """)] is undefined".
Here I am using props to send the nav-menu-title to the submenu components. And the submenu component get the nav-menu-title from the parent component. I verified that using alert(`"${navtitle}"`) The problem is I can't access the variable navtitle at this line of my code allSubMenuItem[`"${navtitle}"`].map((data, index) =>
How can I access the props values inside of allSubMenuItem[].map() ?
You need to remove double qoutation marks while dynamically getting the object key
<ul>
{
allSubMenuItem[`${navtitle}`].map((data, index) => (
menuBody(index, data.title)
))
}
</ul>
Here is your complete Navbar.
import React from "react";
import SubMenu from "./submenu";
function Navbar() {
return (
<>
<ul>
<li>
<span> Home </span>
</li>
<li>
<span> Category </span>
<SubMenu subtitle="Category" />
</li>
<li>
<span> Pages </span>
<SubMenu subtitle="Pages" />
</li>
</ul>
</>
);
}
export default Navbar;
function SubMenu(props) {
const navtitle = props.subtitle;
const allSubMenuItem = {
Category: [
{
id: 1,
title: "Grocery"
},
{
id: 2,
title: "Fashion"
},
{
id: 3,
title: "Electronics"
}
],
Pages: [
{
id: 1,
title: "Abouts Us"
},
{
id: 2,
title: "Contact us"
},
{
id: 3,
title: "Term & Conditions"
}
]
};
function menuBody(index, title) {
const sid = index;
const stitle = title;
return (
<>
<li key={sid}>
<span> {stitle} </span>
</li>
</>
);
}
return (
<>
<ul>
{allSubMenuItem[`${navtitle}`].map((data, index) =>
menuBody(index, data.title)
)}
</ul>
</>
);
}
allSubMenuItem is an object. Hence if you want to map through it, use need to use Object.keys(myObject).map()
So, your submenu.js should probably look like this
import React from "react";
function SubMenu (props) {
const navtitle = props.subtitle;
const allSubMenuItem = {
Category: [
{
id: 1,
title: "Grocery",
},
{
id: 2,
title: "Fashion",
},
{
id: 3,
title: "Electronics",
}
]
Pages: [
{
id: 1,
title: "Abouts Us",
},
{
id: 2,
title: "Contact us",
},
{
id: 3,
title: "Term & Conditions",
}
]
}
function menuBody (index, title) {
const sid = index;
const stitle = title;
return (
<>
<li key={ sid }>
<span> { stitle } </span>
</li>
</>
);
};
return (
<>
<ul>
{
Object.keys(allSubMenuItem).map((key)=>{
allSubMenuItem[key].map((data, index) => (
menuBody(index, data.title)
))
})
}
</ul>
</>
);
}
export default SubMenu;

how can I access an array inside an object? [duplicate]

This question already has answers here:
How to Render Nested Map Items inside react component
(2 answers)
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 8 months ago.
So, I have a file data.js where there is an array with some navigation items.
export const footerNavigation = [
{
category: 'Resources',
items: [
{
href: '',
text: 'Guides'
},
{
href: '',
text: 'Blog'
},
{
href: '',
text: 'Customer stories'
},
{
href: '',
text: 'Glossery'
}
]
},
{
category: 'Resources',
items: [
{
href: '',
text: 'Guides'
},
{
href: '',
text: 'Blog'
},
{
href: '',
text: 'Customer stories'
},
{
href: '',
text: 'Glossery'
}
]
},
];
Footer.jsx
import React from 'react';
import { footerNavigation } from '../data/data';
const Footer = () => {
return (
<div>
{footerNavigation.map((item, index) => {
return (
<div key={index}>
<h3 className='text-lg font-bold mb-8'>{item.category}</h3>
<ul>
<li>
</li>
</ul>
</div>
)
})}
</div>
)
}
export default Footer;
The task is to make sure there is an href value inside a link tag like <a href={.href}</a> and the value of the item here <a>{.value}</a>
I have a basic understanding how to map items that are in the initial array, but have no clue how to map the array that is inside an object which is inside the initial array.
You only missing another items iteration:
const Footer = () => {
return (
<div>
{/* category unique acts as key */}
{footerNavigation.map(({ category, items }) => {
return (
<div key={category}>
<h3 className="text-lg font-bold mb-8">{category}</h3>
<ul>
{/* text unique should be a key */}
{items.map(({ href, text }) => (
<a key={text} href={href}>
{text}
</a>
))}
</ul>
</div>
);
})}
</div>
);
};

React Js: .map doesnt render anything

I have this Array
const Routes = [
{
name: "Google Marketing Platform",
roles: ['Devs', 'Adops', ' Planning'],
module: "",
icon: '',
subMenu: [
{
name: "Campaign Manager",
roles: ['Devs', 'Adops', ' Planning'],
module: "GMP",
icon: '',
subMenu: [
{
name: "Campaign Builder",
roles: ['Devs', 'Adops', ' Planning'],
module: "webcb",
icon: '',
subMenu: []
},
{
name: "Reporting",
roles: ['Devs', 'Adops', ' Planning'],
module: "cmReporting",
icon: '',
subMenu: []
}
]
}
]
and i try to render that array on this code
const NavBar = () => {
const [rutas, setRutas] = useState(Routes);
return (
<div className={`navBar__menu ${isNavOpen} `}>
<ul className='navBar__menu-list'>
{rutas.map((data) => {<>
<Link to={`/${data.module}`} className={"navBar__menu-list-subItems"}>
<p className={"navBar__menu-list-items-text"} >{data.name}</p>
</Link>
</>
{
data.subMenu.map((data) => {
<Link to={`/${data.module}`} className={"navBar__menu-list-subItems"}>
<p className={"navBar__menu-list-items-text"} >{data.name}</p>
</Link>
}
)
}
})}
</ul>
</div>
)
}
Elements are not rendering and idk why, becouse i dont have any error on console and i tried with another array and doesnt work either, thanks you for your help!
You're not returning anything from map. You can either change the {} to () like so
rutas.map((data) => (
<>
...everything else
<>
)
or you can use an explicit return
rutas.map((data) => {
return (
<>
...everything else
<>
)
}

Building a todo app in React, used filters but need a way to make sure "complete" button only removes one task instead of two

I'm new to learning react so I followed this tutorial https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components to create a todo app and then tweaked it to fit the requirements of the project I'm working on. Everything works the way it should except when I delete (complete) things from the associate side, it also deletes it from my main side as well. I understand the general concept of why that's happening (I don't have two separate lists in my code), just not sure how to go about fixing it without removing the filter I have in place. I had tried to implement a separate list for those tasks but just wasn't understanding how to go about it.
Added CodeSandBox for more context: https://codesandbox.io/s/hungry-sky-5f482?file=/src/index.js
Check task items and then view the items you've checked in "Show Associate Tasks." The issue is completing a task on the associate side also completes it on the "Show All Tasks" side.
App.js
const FILTER_MAP = {
All: () => true,
Associate: task => task.checked
};
const FILTER_NAMES = Object.keys(FILTER_MAP);
function App(props) {
const [tasks, setTasks] = useState(props.tasks);
const [filter, setFilter] = useState('All');
function toggleTaskChecked(id) {
const updatedTasks = tasks.map(task => {
if (id === task.id) {
return {...task, checked: !task.checked}
}
return task;
});
setTasks(updatedTasks);
}
function completeTask(id) {
const remainingTasks = tasks.filter(task => id !== task.id);
setTasks(remainingTasks);
}
const taskList = tasks
.filter(FILTER_MAP[filter])
.map(task => (
<Todo
id={task.id}
name={task.name}
checked={task.checked}
key={task.id}
toggleTaskChecked={toggleTaskChecked}
completeTask={completeTask}
/>
));
const filterList = FILTER_NAMES.map(name => (
<FilterButton
key={name}
name={name}
isPressed={name === filter}
setFilter={setFilter}
/>
));
function addTask(name) {
const newTask = { id: "todo-" + nanoid(), name: name, checked: false };
setTasks([...tasks, newTask]);
}
return (
<div className="app">
<h1 className = "tasks-header">Task Tracker</h1>
<Form addTask={addTask}/>
<div className="list-buttons">
{filterList}
</div>
<ul
role="list"
className="todo-list"
aria-labelledby="list-heading"
>
{taskList}
</ul>
</div>
);
}
export default App
Todo.js
export default function Todo(props) {
return (
<li className="todo stack-small">
<div className="c-cb">
<input id={props.id}
type="checkbox"
defaultChecked={props.checked}
onChange={() => props.toggleTaskChecked(props.id)}
/>
<label className="todo-label" htmlFor="todo-0">
{props.name}
</label>
</div>
<div className="btn-group">
<button type="button"
className="complete-button"
onClick={() => props.completeTask(props.id)}
>
Complete
</button>
</div>
</li>
);
}
index.js
const DATA = [
{ id: "todo-0", name: "Brush Teeth", checked: false },
{ id: "todo-1", name: "Make Dinner", checked: false },
{ id: "todo-2", name: "Walk Dog", checked: false },
{ id: "todo-3", name: "Run Reports", checked: false },
{ id: "todo-4", name: "Visit Mom", checked: false },
{ id: "todo-5", name: "Aerobics", checked: false },
{ id: "todo-6", name: "Project", checked: false },
{ id: "todo-7", name: "Lecture", checked: false },
{ id: "todo-8", name: "Have Lunch", checked: false }
];
ReactDOM.render(
<React.StrictMode>
<App tasks={DATA}/>
</React.StrictMode>,
document.getElementById('root')
);
FilterButton.js
function FilterButton(props) {
return (
<button
type="button"
className="toggle-btn"
aria-pressed={props.isPressed}
onClick={() => props.setFilter(props.name)}
>
<span className="visually-hidden">Show </span>
<span>{props.name}</span>
<span className="visually-hidden"> Tasks</span>
</button>
);
}
export default FilterButton;
We have 3 boolean fields: checked, completed, completedAssoc.
{
id: "todo-0",
name: "Brush Teeth",
checked: false,
completed: false,
completedAssoc: false
},
The filters will work as follows:
const FILTER_MAP = {
All: (task) => !task.completed,
Associate: (task) => task.checked && !task.completedAssoc
};
And finally changes in completeTask and addTask:
function completeTask(id) {
const complField = filter === "All" ? "completed" : "completedAssoc";
const updatedTasks = tasks.map((task) =>
id === task.id ? { ...task, [complField]: true } : task
);
setTasks(updatedTasks);
}
function addTask(name) {
const newTask = {
id: "todo-" + nanoid(),
name: name,
checked: false,
completed: false,
completedAssoc: false
};
setTasks([...tasks, newTask]);
}

ReactJs useState Array Map

Hello I have doubts on how I can do this in react using useState,
basically i have this menu where i need to map, i basically need a state containing all tags, and with boolean state true or false to know if the current item is active, and i will make it active by clicking on the item, and deactivate it when another item is clicked
that is, only one menu item active at a time
export const SideBarTags = [
{
name: 'Tutoriais',
link: '../tutorials',
icon: faFileAlt,
dropdownItems: null,
active: false,
},
{
name: 'Avisos',
link: '../news',
icon: faNewspaper,
dropdownItems: null,
active: false,
},
{
name: 'Serviços',
link: '../services',
icon: faMeteor,
active: false,
dropdownItems: [
{ name: 'Elo Boost', link: '/eloBost' },
{ name: 'Duo Boost', link: '/duoBoost' },
{ name: 'MD10', link: '/eloBost' },
{ name: 'Coaching', link: '/duoBoost' },
{ name: 'Vitóriais', link: '/duoBoost' },
],
},
{
name: 'Carteira',
link: '../cartcredit',
icon: faWallet,
active: false,
dropdownItems: [
{ name: 'Histórico', link: '/history' },
{ name: 'Adicionar Crédito', link: '/add' },
],
},
];
and my TSX:
const MenuTags: React.FC<Hamburguer> = ({ isOpen }) => {
const [menuTags, setMenuTags] = useState(SideBarTags.map());
return (
<DashMenu open={isOpen}>
<MenuItem /> //(this is my tag <li>
</DashMenu>
);
};
const MenuItem: React.FC = () => {
return (
<ListItem>
<ListWrap
>
<a>
<FontAwesomeIcon
className="icon-li"
icon={icon}
size={isOpen ? 'lg' : 'lg'}
fixedWidth
color="white"
/>
<span
className="li-name"
>
{name}
</span>
</a>
</ListItem>
);
};
Component logic if you wanted to map the menu items with the active item
const [menuItems, setMenuItems] = useState(SideBarTags);
const clickHandler = name => () => {
setMenuItems(items =>
items.map(item => ({
...item,
active: item.name === name
}))
);
};
...
{menuItems.map(item => (
<li
key={item.name}
className={item.active ? "active" : ""}
onClick={clickHandler(item.name)}
>
{item.name}
</li>
))}
CSS
.active {
// could be whatever style you need
color: red;
}
Super simplified version of what I did in a past project:
const MenuTags = () => {
const [selectedLink, setSelectedLink] = useState(null)
return (
<ul>
{SideBarTags.map((obj) => (
<li className={`${selectedLink === obj.name ? 'link--selected' : ''}`}>
<a
onClick={() => {
setSelectedLink(obj.name)
}}
href={obj.link}
>
{obj.name}
</a>
</li>
))}
</ul>
)
}
Use CSS to open and close the menu items, by having a class such as 'link--selected' added to an element you can just show that item.

Categories