Set onclick event on React list (Nav Bar) - reactjs

I am trying to implement onclick into navbar items so when i click one of the items in nav bar, it will load some component.
below is the list for the navbar:
class Navbar extends React.Component {
render () {
const menuItems = [
{
title: 'Home',
url: '/',
},
{
title: 'Assets',
url: '/Assets',
},
{
title: 'Service Report',
url: '/Servicereport',
},
{
title: 'Change Request',
url: '/Changerequest',
},
{
title: 'Logout',
url: '/logout',
}
];
return (
<nav>
<ul className="menus">
{menuItems.map((menu, index) => {
return (
<MenuItems
items={menu}
key={index}
/>
);
})}
</ul>
</nav>
);
};
}
export default Navbar;
How can i implement?
If need more information, please let me know. thank you.
I am trying to implement onclick into navbar items

You can try the onClick event in React and call a function to redirect to the specified location using useNavigate hook of React Router. You first need to install react router DOM.
npm i react-router-dom
Try the below code for redirecting
import { useNavigate } from "react-router-dom";
class Navbar extends React.Component {
render () {
const menuItems = [
{
title: 'Home',
url: '/',
},
{
title: 'Assets',
url: '/Assets',
},
{
title: 'Service Report',
url: '/Servicereport',
},
{
title: 'Change Request',
url: '/Changerequest',
},
{
title: 'Logout',
url: '/logout',
}
];
const navigate = useNavigate();
return (
<nav>
<ul className="menus">
{menuItems.map((menu, index) => {
return (
<MenuItems
onClick = {() => navigate(`${menu.url}`)}
items={menu}
key={index}
/>
);
})}
</ul>
</nav>
);
};
}
export default Navbar;

You just have to add an onclick attributes to your MenuItems.
class Navbar extends React.Component {
const handleOnClick = (e) => {
if(e.currentTarget == "Something") {
// Do something
} else {
// Do something else
}
}
render () {
const menuItems = [
{
title: 'Home',
url: '/',
},
{
title: 'Assets',
url: '/Assets',
},
{
title: 'Service Report',
url: '/Servicereport',
},
{
title: 'Change Request',
url: '/Changerequest',
},
{
title: 'Logout',
url: '/logout',
}
];
return (
<nav>
<ul className="menus">
{menuItems.map((menu, index) => {
return (
<MenuItems
items={menu}
key={index}
onClick={handleOnClick}
/>
);
})}
</ul>
</nav>
);
};
}
export default Navbar;

<MenuItems onClick={() => {…}} {…this.props} /> attach onClick handler as a prop. Is MenuItems defined? I would change the name to MenuItem.

Related

Why Jotai makes a component infinitely re-render when i define the related atom as a function with parameter?

i'm trying to implement language state on my website with Jotai and React.
I tried to do something and it worked but the problem is that the the React component started to re-render infinitely.
Here is what i did :
This is a separate file i created for defining atoms,
import { atom } from "jotai";
export const languageState = atom('tr')
export const marineHeaderObject = (langState) => atom({
buttonDataArray: [
{ label: langState === 'tr' ? 'Anasayfa' : 'Home ', path: 'home', isDropdown: false },
{ label: langState === 'tr' ? 'Kurumsal' : 'Corporate ' , path: '', isDropdown: false},
{ label: langState === 'tr' ? 'Modellerimiz' : 'Models ', path: '', isDropdown: true},
{ label: langState === 'tr' ? 'Tekne Kiralama' : 'Boat Rental ', path: '', isDropdown: false},
{ label: langState === 'tr' ? 'Güverte Kaplama' : 'Deck Covering ', path: '', isDropdown: false},
{ label: langState === 'tr' ? 'İletişim' : 'Contact ', path: 'contact', isDropdown: false},
],
logoPath: ''
}
)
This is where i use those atoms,
import { useEffect, useState } from 'react'
import { useAtom } from 'jotai'
import Header from '../../components/Header';
import { marineHeaderObject } from '../../states';
import { languageState } from '../../states';
const myPage = () => {
const [langState] = useAtom(languageState)
const [marineHeader] = useAtom(marineHeaderObject(langState))
return (
<div className="pageWrapper">
<Header headerObject={marineHeader} />
</div>
);
}
export default myPage;
And this is the Header component below,
import NavButton from './NavButton'
const Header = ({ headerObject }) => {
return (
<div className="header">
<div className="logo">
<a> <img height="70" width="184" src="../assets/images/logov2.svg" /> </a>
</div>
<div className="navigationBar">
<nav>
{
headerObject.buttonDataArray.map((button, index) => {
return (
<NavButton key={index} buttonData={button} />
)
})
}
</nav>
</div>
</div>
);
}
export default Header;
And this is what browser console gives me :
270react_devtools_backend.js:4026 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
at Maycotex (http://localhost:3000/main.9f8185fe9033d1ee3856.hot-update.js:36:69)
at Routes (http://localhost:3000/static/js/bundle.js:41590:5)
at Router (http://localhost:3000/static/js/bundle.js:41523:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:40332:5)
at App
What's the problem here ? Why React re-renders the component infinitely ?
atom() should not be called within a functional component. Place it outside of your functional component.

Converting to Typescript: Passing mapped props

I am struggling with converting the following React.JS script to TypeScript. Can anyone help? I am trying to make a drop down nav bar in my website.
This is my Header.tsx file:
I am getting a red squiggly line on onClick={closeMobileMenu} - Property 'onClick' does not exist on type 'IntrinsicAttributes & { items: any; }'.
<ul className="navbar-nav">
{menuItems.map((menu, index) => {
return (
<MenuItems
items={menu}
key={index}
onClick={closeMobileMenu}
/>
);
})}
</ul>
This is my Menu.tsx file
I am getting an error on
"items": Binding element 'items' implicitly has an 'any' type
"contains":Property 'contains' does not exist on type 'never'
import React, { useState, useEffect, useRef } from "react";
import {HashLink} from "react-router-hash-link";
import Dropdown from "./Dropdown";
import "./Header.css";
interface MenuItems {
items: string
key: number
onClick: (param: any) => void
}
const MenuItems = ({ items }) => {
let ref = useRef();
const [dropdown, setDropdown] = useState(false);
const onMouseEnter = () => {
window.innerWidth > 960 && setDropdown(true);
};
const onMouseLeave = () => {
window.innerWidth > 960 && setDropdown(false);
};
useEffect(() => {
const handler = (event: { target: any; }) => {
if (dropdown && ref.current && !ref.current.contains(event.target)) {
setDropdown(false);
}
};
document.addEventListener("mousedown", handler);
document.addEventListener("touchstart", handler);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", handler);
document.removeEventListener("touchstart", handler);
};
}, [dropdown]);
return (
<li
className="nav-item"
ref={ref}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={() => setDropdown(false)}
>
{items.submenu ? (
<>
<button
type="button"
aria-haspopup="menu"
aria-expanded={dropdown ? "true" : "false"}
>
<HashLink smooth to={items.path} className="nav-link">
{items.title} <i className="fas fa-chevron-down"></i>
</HashLink>
</button>
<Dropdown submenus={items.submenu} dropdown={dropdown} />
</>
) : (
<HashLink
smooth to={items.path}
className="first-level-nav-link"
>
{items.title}
</HashLink>
)}
</li>
);
};
export default MenuItems;
This is my menuItems.tsx file:
export const menuItems = [
{
title: "Home",
path: "/",
cName: "nav-link",
submenu: [
{
title: "Story",
path: "/#story",
cName: "nav-link",
},
{
title: "Map",
path: "/#map",
cName: "nav-link",
}
],
},
{
title: "Rewards",
path: "/",
cName: "nav-link",
submenu: [
{
title: "competition",
path: "competition",
cName: "nav-link",
},
{
title: "prizes",
path: "prizes",
cName: "nav-link",
}
],
},
{
title: "Downloads",
path: "downloads",
cName: "nav-link",
}
];
For error #2
TypeScript cannot actually infer how you intend to use this ref without any extra information.
const ref = useRef() // React.MutableRefObject<undefined>
However, useRef can be used as a generic to tell TypeScript how you do intend on using the ref.
const ref = useRef<HTMLLIElement>(null) // React.MutableRefObject<HTMLLIElement>
Only then will TypeScript allow you to access ref.current.contains, because it knows that the contains property exists on a HTMLLIElement node.
Correction #1: For function reference as a parameter you need to define any data type
interface MenuItems {
items: any,
key: number,
onClick: any
}
Correction #2 : At your MenuItems Component.
const MenuItems = (props: MenuItems ) => {
//access menu item
console.log(props.items.title);
}

how to give navigation for list in horizontal menu of reactjs

Can any one help to get the solution for this question, for page navigation i am using react-horizontal-scrolling-menu. in the reactjs application. Just i want give page navigation where i should give navigation please tell me. this code has giving by the link https://https://www.npmjs.com/package/react-horizontal-scrolling-menu
import React, { Component } from 'react';
import ScrollMenu from 'react-horizontal-scrolling-menu';
import './App.css';
// list of items
const list = [
{ name: 'item1' },
{ name: 'item2' },
{ name: 'item3' },
{ name: 'item4' },
{ name: 'item5' },
{ name: 'item6' },
{ name: 'item7' },
{ name: 'item8' },
{ name: 'item9' }
];
// One item component
// selected prop will be passed
const MenuItem = ({text, selected}) => {
return <div
className={`menu-item ${selected ? 'active' : ''}`}
>{text}</div>;
};
// All items component
// Important! add unique key
export const Menu = (list, selected) =>
list.map(el => {
const {name} = el;
return <MenuItem text={name} key={name} selected={selected} />;
});
const Arrow = ({ text, className }) => {
return (
<div
className={className}
>{text}</div>
);
};
const ArrowLeft = Arrow({ text: '<', className: 'arrow-prev' });
const ArrowRight = Arrow({ text: '>', className: 'arrow-next' });
const selected = 'item1';
class App extends Component {
constructor(props) {
super(props);
// call it again if items count changes
this.menuItems = Menu(list, selected);
}
state = {
selected
};
onSelect = key => {
this.setState({ selected: key });
}
render() {
const { selected } = this.state;
// Create menu from items
const menu = this.menuItems;
return (
<div className="App">
<ScrollMenu
data={menu}
arrowLeft={ArrowLeft}
arrowRight={ArrowRight}
selected={selected}
onSelect={this.onSelect}
/>
</div>
);
}
}
'css code start here '
.menu-item {
padding: 0 40px;
margin: 5px 10px;
user-select: none;
cursor: pointer;
border: none;
}
.menu-item-wrapper.active {
border: 1px blue solid;
}
.menu-item.active {
border: 1px green solid;
}
.scroll-menu-arrow {
padding: 20px;
cursor: pointer;
}
You are missing specified paths (or what resolves to pathnames) from your list of routes that are passed to the Link component's to prop.
// list of items
const list = [
{ name: "item1", path: "/" },
{ name: "item2" }, // MISSING path properties!
{ name: "item3", path: "./abcd" },
{ name: "item4" },
{ name: "item5" },
{ name: "item6" },
{ name: "item7" },
{ name: "item8" },
{ name: "item9", path: "./example_1" }
];
// One item component
// selected prop will be passed
const MenuItem = ({ text, path, selected }) => {
return (
<div className={`menu-item ${selected ? "active" : ""}`}>
<NavLink exact to={path}> // All links must have a defined to prop
{text}
</NavLink>
</div>
);
};
It is a simple fix to add a defined path for each route in your config. For example:
const list = [
{ name: "item1", path: "/" },
{ name: "item2", path: "/page/1" },
{ name: "item3", path: "/abcd" },
{ name: "item4", path: "/page/2" },
{ name: "item5", path: "/page/3" },
{ name: "item6", path: "/page/4" },
{ name: "item7", path: "/page/42" },
{ name: "item8", path: "/example_1" },
{ name: "item9", path: "/page/5" }
];
DEMO I've taken the liberty of forking your sandbox, updated to specify paths, and only define the menu once and display in one location (DRY principle) in your root App.
Link to this library is not working.
You can add another property to your list like { name: 'item1', url: '/somecomponenturl' }
Then in your Menu function just pass the URL as prop just like text prop and in MenuItem function use your url with Link or NavLink like:
const MenuItem = ({text, url, selected}) => {
return <div
className={`menu-item ${selected ? 'active' : ''}`}
><Link to={url}>{text}</Link></div>;
};
export const Menu = (list, selected) =>
list.map(el => {
const {name} = el;
const {url} = el;
return <MenuItem text={name} url={url} key={name} selected={selected} />;
});

react-photo-gallery don't change photos via state

The photo object is not changing in the Gallery component via state.
I created a "folder gallery", a multi gallery component to render a new one if you select it by click.
See the demonstration:
https://codesandbox.io/s/50wr02n8q4
github issue:
https://github.com/neptunian/react-photo-gallery/issues/118
Envs:
react-photo-gallery: 6.2.2
react: 16.6.3
npm: 6.4.1
See the code below:
const folderPhotos = [
{
title: "Gallery one",
photos: [
{
src: "https://source.unsplash.com/2ShvY8Lf6l0/800x599"
},
{
src: "https://source.unsplash.com/Dm-qxdynoEc/800x799"
},
{
src: "https://source.unsplash.com/qDkso9nvCg0/600x799"
}
]
},
{
title: "Gallery two",
photos: [
{
src: "https://source.unsplash.com/iecJiKe_RNg/600x799"
},
{
src: "https://source.unsplash.com/epcsn8Ed8kY/600x799"
},
{
src: "https://source.unsplash.com/NQSWvyVRIJk/800x599"
}
]
}
];
The photoProps dimensions object:
const photoProps = {
width: 1,
height: 1
};
The internal component here:
<Gallery
columnsLength={6}
photosObj={folderPhotos}
photoProps={photoProps}
withLightbox={true}
/>
Now the Gallery component:
import _ from "lodash";
import React, { Component, Fragment } from "react";
import Gallery from "react-photo-gallery";
import Lightbox from "react-images";
export class PhotoGallery extends Component {
constructor(props) {
super(props);
// Bindables
this.showGallery = this.showGallery.bind(this);
this.openLightbox = this.openLightbox.bind(this);
this.closeLightbox = this.closeLightbox.bind(this);
this.goToPrevious = this.goToPrevious.bind(this);
this.goToNext = this.goToNext.bind(this);
}
state = {
photosObj: [],
currentImage: 0,
lightboxIsOpen: false
};
async showGallery(itemObj, photoProps) {
let photosObj = [];
if (!_.isEmpty(itemObj)) {
photosObj = await Object.keys(itemObj)
.map(i => itemObj[i])
.map(item => ({
...item,
width: photoProps.width,
height: photoProps.height
}));
this.setState({
photosObj
});
console.log("-- props: ", this.state.photosObj);
}
}
openLightbox(event, obj) {
this.setState({
currentImage: obj.index,
lightboxIsOpen: true
});
}
closeLightbox() {
this.setState({
currentImage: 0,
lightboxIsOpen: false
});
}
goToPrevious() {
this.setState({
currentImage: this.state.currentImage - 1
});
}
goToNext() {
this.setState({
currentImage: this.state.currentImage + 1
});
}
render() {
const { columnsLength, photosObj, photoProps, withLightbox } = this.props;
return (
<div className="section-body">
<div className="content-col-info">
<ul className="list-mentors w-list-unstyled">
{photosObj.map((itemObj, i) => (
<li key={i}>
<span
className="mentors-item w-inline-block"
onClick={() => this.showGallery(itemObj.photos, photoProps)}
>
<div>{itemObj.title}</div>
</span>
</li>
))}
</ul>
</div>
<div className="content-col-info">
{!_.isEmpty(this.state.photosObj) && (
<Gallery
columns={columnsLength}
photos={this.state.photosObj}
onClick={withLightbox ? this.openLightbox : null}
/>
)}
{!_.isEmpty(this.state.photosObj) && withLightbox && (
<Lightbox
images={this.state.photosObj}
onClose={this.closeLightbox}
onClickPrev={this.goToPrevious}
onClickNext={this.goToNext}
currentImage={this.state.currentImage}
isOpen={this.state.lightboxIsOpen}
/>
)}
</div>
</div>
);
}
}
export default PhotoGallery;
EDIT - I updated the Gallery props
I fix the Gallery component props of the example too.
loadGallery(columnsLength) {
import("./photo-gallery").then(Gallery => {
console.log("-- load gallery: ", this.state.photosObj);
return (
<Gallery.default
columnsLength={columnsLength}
photosObj={this.state.photosObj}
withLightbox={true}
/>
);
});
}
And to call this:
{!_.isEmpty(this.state.photosObj) && this.loadGallery(columnsLength)}
References:
Intro to Dynamic import() in Create React App
React images
Since photos option is required in Gallery.js (Library)
Gallery.propTypes = {
photos: _propTypes2.default.arrayOf(_Photo.photoPropType).isRequired,
direction: _propTypes2.default.string,
onClick: _propTypes2.default.func,
columns: _propTypes2.default.number,
margin: _propTypes2.default.number,
ImageComponent: _propTypes2.default.func
};
Pass "photos={this.state.photosObj}" in <Gallery /> of Gallery.js (your js file) as follows:
Code:
<Gallery
columnsLength={columnsLength}
photosObj={this.state.photosObj}
photos={this.state.photosObj}
withLightbox={true}
/>
I am not sure why you pass other options.

React Material UI makeSelectable List Not Compatible With Nested ListItem Component

I have a problem that my makeSelectable code is not working when I used the ListItem from child component instead of using ListItem directly. Here is my example code (the workingLinkItems can be selected normally but the notWorkingLinkItems is not selectable).
import React, { Component, PropTypes } from 'react'
import { List, makeSelectable, ListItem } from 'material-ui/List'
import { wrapState } from 'helpers/utils'
const { func, shape, string, number } = PropTypes
class TryListItem extends Component {
static propTypes = {
onOpenLink: func.isRequired,
linkItem: shape({
key: number.isRequired,
link: string.isRequired,
text: string.isRequired,
}).isRequired,
}
handleOpenLink = () => {
this.props.onOpenLink(this.props.linkItem.link)
}
render () {
const { key, link, text } = this.props.linkItem
return <ListItem
value={key}
primaryText={text}
onTouchTap={this.handleOpenLink} />
}
}
let SelectableList = makeSelectable(List)
SelectableList = wrapState(SelectableList)
class TrySelectableList extends Component {
handleOpenLink = (location) => {
console.log('The page will be redirected to: ', location)
}
render () {
const links = [
{
key: 1,
link: '/home',
text: 'Home',
},
{
key: 2,
link: '/about',
text: 'About Us',
},
{
key: 3,
link: '/contact',
text: 'Contact Us',
},
]
const notWorkingLinkItems = links.map((link) => (
<TryListItem
onOpenLink={this.handleOpenLink}
linkItem={link} />
))
const workingLinkItems = links.map((link) => (
<ListItem
value={link.key + 3}
primaryText={link.text}
onTouchTap={this.handleOpenLink} />
))
return (
<div>
<SelectableList defaultValue={1}>
{notWorkingLinkItems}
{workingLinkItems}
</SelectableList>
</div>
)
}
}
export default TrySelectableList
Any idea what's wrong with my code?
Finally after a lot of trial tinkering with material-ui makeSelectable function. I ended up creating my own selectable style.
Here is my code solution for my problem:
import React, { Component, PropTypes } from 'react'
import { List, ListItem } from 'material-ui/List'
const { bool, func, shape, string, number } = PropTypes
class TryListItem extends Component {
static propTypes = {
selected: bool.isRequired,
onOpenLink: func.isRequired,
linkItem: shape({
link: string.isRequired,
text: string.isRequired,
}).isRequired,
}
handleOpenLink = () => {
this.props.onOpenLink(this.props.linkItem.link, this.props.linkItem.key)
}
render () {
const { key, link, text } = this.props.linkItem
const styles = this.props.selected
? { backgroundColor: 'rgba(0, 0, 0, 0.2)', }
: {}
return <ListItem
primaryText={text}
onTouchTap={this.handleOpenLink}
style={styles} />
}
}
class TrySelectableList extends Component {
componentWillMount = () => {
this.buildLink()
}
buildLink = (selectedIndex = 0) => {
const initialLinks = [
{
key: 1,
link: '/home',
text: 'Home',
selected: false,
},
{
key: 2,
link: '/about',
text: 'About Us',
selected: false,
},
{
key: 3,
link: '/contact',
text: 'Contact Us',
selected: false,
},
]
const links = initialLinks.map((link) => {
if (link.key === selectedIndex) {
link.selected = true
}
return link
})
this.setState({
links: links
})
}
handleOpenLink = (location, index) => {
console.log('The page will be redirected to: ', location)
this.buildLink(index)
}
render () {
const workingLinkItems = this.state.links.map((link) => (
<TryListItem
key={link.key}
selected={link.selected}
onOpenLink={this.handleOpenLink}
linkItem={link} />
))
return (
<div>
<List>
{workingLinkItems}
</List>
</div>
)
}
}
export default TrySelectableList

Resources