As of now, I have a class generating all the list for a component that will be called multiple times. However is it possible for the user to only select one item with the same ID?
class Skills extends Component {
constructor(props) {
super(props);
this.img = props.src
this.name = props.name
}
render() {
return (
<React.Fragment>
<div>
<Row>
<Image src={this.img} style={skillIcon} rounded />
<div>
<h4>{this.name}</h4>
<ul className="sul">
<li id="lvl 1">1</li>
<li id="lvl 2">2</li>
<li id="lvl 3">3</li>
<li id="lvl 4">4</li>
<li id="lvl 5">5</li>
<li id="lvl 6">6</li>
<li id="lvl 7">7</li>
<li id="lvl 8">8</li>
<li id="lvl 9">9</li>
<li id="lvl 10">10</li>
<li id="lvl 11">11</li>
<li id="lvl 12">12</li>
<li id="lvl 13">13</li>
<li id="lvl 14">14</li>
<li id="lvl 15">15</li>
</ul>
</div>
</Row>
</div>
</React.Fragment>
);
}
}
For example, if li with id lvl 1 is selected, the next time someone clicks on another lvl 1 li of the same regenerated component, the previous selection would be deselected.
You need to store user selection in state and update state when user selected another item.
State can be handled in two ways.
Classic Class components way
// its better to have a array with levels
const levels = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
class Skills extends Component {
constructor(props) {
super(props);
this.state = {
selection:null
}
this.img = props.src
this.name = props.name
}
render() {
const {selection} = this.state
return (
<React.Fragment>
<div>
<Row>
<Image src={this.img} style={skillIcon} rounded />
<div>
<h4>{this.name}</h4>
<ul className="sul">
{levels.map((level) => {
const isSelected = selection === level;
return (
<li
id={`lvl ${level}`}
key={level}
onClick={() => setSelection(level)}
style={{
padding: isSelected ? 10 : 2,
backgroundColor: isSelected ? 'red' : 'white',
}}
>
{level}
</li>
);
})}
</ul>
</div>
</Row>
</div>
</React.Fragment>
);
}
}
new functional hooks way
function Skills() {
const [selection, setSelection] = useState(null);
return (
<>
<div>
<img src={img} />
<div>
<h4>{name}</h4>
<ul className="sul">
{levels.map((level) => {
const isSelected = selection === level;
return (
<li
id={`lvl ${level}`}
key={level}
onClick={() => setSelection(level)}
style={{
padding: isSelected ? 10 : 2,
backgroundColor: isSelected ? 'red' : 'white',
}}
>
{level}
</li>
);
})}
</ul>
</div>
</div>
</>
);
}
Not related but i just wanna suggest
1. If src is not dynamic , just assign a const variable assigned like
const LOGO_URL = "url to logo"
const logoStyle = {
...some css
}
<Image src={LOGO_URL} style={logoStyle} />
2. If this array of level numbers is dynamic (some api or something) , get it from props
function Skill(props){
const { levels } = props
return (
<>
{levels.map(i => {
...do anything
})}
</>
)
}
update: when you need to update parent State from child component
function RootComponent() {
const [rootState,setRootState] = useState(null)
return (
<SkillsOrAnyComponent updateState={setRootState} />
)
function SkillsOrAnyComponent(props){
const { updateState } = props
const changeMainState = (val) => {
updateState(val)
}
return ...some jsx
}
}
Related
I have list of data that render it with map - I need to add an event just in one of the item from that list.
const UserModal = (props) => {
const {user,setUser} = props ;
const list = [,{id:3,text:'گفت وگو ها',icon:<BsChat />},{id:5,text:'خروج',icon:<BiExit />},];
/this is my list for making navigation bar
return (
<div className={style.main}>
<div style={{bordeBottom:'1px solid black'}}>
<BiUser />
<p>{user.username}</p>
</div>
{ //this is where I render a list to show and make component
list.map((item)=>
<div key={item.id}>
{item.icon}
<p>{item.text}</p>
</div>)
}
</div>
);
};
export default UserModal;
this my code and for example I need to add an event on specific object that has id=5 in that list .
how can I do that
I don't know if there is some sort of built-in solution for this, but here is a simple workaround:
I changed a few things for simplicity's sake
The important part is the if statement with checks if item ID is 5 then if so adds a div with the desired event
function App() {
const list = [
,
{ id: 3, text: "comonent 3" },
{ id: 5, text: "comonent 5 (target)" }
];
return (
<>
<h1>Hello world<h1/>
{list.map((item) => (
<div key={item.id} style={{ backgroundColor: "red" }}>
<p>{item.text}</p>
{item.id == 5 ? (
<div
onClick={() => {
alert("This component has a event");
}}
>
{" "}
event
</div>
) : (
<></>
)}
</div>
))}
</>
);
}
const UserModal = (props) => {
const {user,setUser} = props ;
const myEvent = () => alert('event fired');
const list = [,{id:3,text:'گفت وگو ها',icon:<BsChat /> , event : myEvent},{id:5,text:'خروج',icon:<BiExit />},];
/this is my list for making navigation bar
return (
<div className={style.main}>
<div style={{bordeBottom:'1px solid black'}}>
<BiUser />
<p>{user.username}</p>
</div>
{ //this is where I render a list to show and make component
list.map((item)=>
<div key={item.id}>
{item.icon}
<p onClick={item.event}>{item.text}</p>
</div>)
}
</div>
);
};
export default UserModal;
list.map((item, i)=> (
item.id == 5 ?
<div onClick={handleClick} key={i}></div>
:
<div key={i}></div>
)
I'm fairly new to React and I'm working on a website for a friend that uses a lot of react features. One thing this website needs is a navbar where every item in the navbar has a dropdown selection of additional nav items. I'm able to both render pages conditionally as independent nav items and create the hover dropdown on each nav item, but my issue comes into merging them together. I've tried a few things such as mapping through props twice, creating a large object where the nav item is a name and the dropdown items are subnames, but neither of those worked.
Here is the code I'm using:
function Nav(props) {
const [navItemList, setNavItemList] = useState([
{name: 'About', dropdownItem1: 'About Me', dropdownItem2: 'About Tampa Bay', id: 1},
]);
const { pages = [], setCurrentPage, currentPage } = props;
return (
<header className="flex-row">
<h1 class="name-tag">
<img src={"../../assets/Logo1.png"} />
</h1>
<nav>
<NavItems items={navItemList} />
<ul className="flex-row nav-list">
{pages.map(navItem => (
<li className={`li-spacing text-format ${currentPage.name === navItem.name && 'navActive'}`} key={navItem.id}>
<span onClick={() => { setCurrentPage(navItem) }}>{navItem.name}</span>
</li>
))}
</ul>
</nav>
</header>
)
}
function App() {
const [pages] = useState([
{
id: 1,
name: 'Home'
},
{
id: 2,
name: 'About Me'
},
{
id: 3,
name: 'About Tampa Bay'
},
])
const [currentPage, setCurrentPage] = useState(pages[0])
return (
<div>
<Nav
pages={pages}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
></Nav>
<main>
<Pages currentPage={currentPage}></Pages>
</main>
</div>
);
}
function NavItems(props) {
const items = props.items
return (
<ul className=" flex-row nav-list">
{/* map through the props so each navitem receives unique information */}
{items.map((navItem) => (
<div className="dropdown" key={navItem.id}>
<li className="nav-list-item">{ navItem.name }</li>
<div className="dropdown-item">
<p>{ navItem.dropdownItem1 }</p>
<p>{ navItem.dropdownItem2 }</p>
</div>
</div>
)) }
</ul>
)
}
export default NavItems;
Something like this maybe? This might need to be adjusted a bit to fit your styling needs.
const pages = {
home: {
name: 'Home',
subPages: {},
},
about: {
name: 'About',
subPages: {
aboutMe: {
name: 'About Me',
},
aboutTampaBay: {
name: 'About Tampa Bay',
},
},
},
}
function App() {
const [currentPageKey, setCurrentPageKey] = useState('home')
return (
<div>
<Nav pages={pages} currentPage={currentPage} setCurrentPageKey={setCurrentPageKey} />
<main>
<Pages currentPage={pages[currentPageKey]} />
</main>
</div>
)
}
function Nav(props) {
const { setCurrentPageKey, currentPage, pages } = props
return (
<header className="flex-row">
<h1 class="name-tag">
<img src={'../../assets/Logo1.png'} />
</h1>
<nav>
<ul className="flex-row nav-list">
{Object.entries(pages).map(([key, { name, subPages }]) => (
<li className={`li-spacing text-format ${currentPage.name === name && 'navActive'}`} key={key}>
<NavItems setCurrentPageKey={setCurrentPageKey} title={name} items={subPages} />
<button onClick={() => setCurrentPageKey(key)}>{name}</button>
</li>
))}
</ul>
</nav>
</header>
)
}
export default function NavItems(props) {
const { setCurrentPageKey, items, title } = props
return (
<ul className="flex-row nav-list">
<div className="dropdown">
<li className="nav-list-item">{title}</li>
<div className="dropdown-item">
{/* map through the props so each navitem receives unique information */}
{Object.entries(items).map(([key, { name }]) => (
<button onClick={() => setCurrentPageKey(key)} key={key}>
{name}
</button>
))}
</div>
</div>
</ul>
)
}
I am very new to react and js,
I have a menu and submenu, I use a list to map data,
I want to write a function, so onmouseover one item in the list,
if it has submenu, it will show.
the problem is that I can't select the submenu using ref.
It is just too complicated for me, any help would be much appreciated!
enter image description here
import React, { Component } from "react";
export class Menu extends Component {
constructor(props) {
super(props);
this.liRefs = [];
}
showSubmenu = (e) => {
// this.liRefs.current.style.display = "block";
for (var i = 0; i < this.liRefs.length; i++) {
this.liRefs[i].current.style.display = "block";
}
// console.log(this.liRefs[10]);
};
getStyle = (e) => {
e.target.style.background = "red";
};
render() {
return (
<ul className="mainmenu">
{this.props.items.map((i) =>
i.subitems ? (
<li key={i.id} onMouseOver={this.showSubmenu}>
{i.icon}
{i.name}
<ul key={i.id} ref={(ref) => (this.liRefs[i.id] = ref)}>
{i.subitems.map((item) => (
<div key={item.id} className="submenu">
{item.icon}
{item.name}
</div>
))}
</ul>
</li>
) : (
<li key={i.id}>
{i.icon}
{i.name}
{i.img}
</li>
)
)}
</ul>
);
}
}
export default Menu;
You are giving ref value to this. liRefs[i.id] and accessing through this. liRefs[i] so that both are the different change your code as below:
{this.props.items.map((i,index) =>
i.subitems ? (
<li key={i.id} onMouseOver={this.showSubmenu}>
{i.icon}
{i.name}
<ul key={i.id} ref={(ref) => (this.liRefs[i.id] = ref)}>
{i.subitems.map((item) => (
<div key={item.id} className="submenu">
{item.icon}
{item.name}
</div>
))}
</ul>
</li>
) : (
<li key={i.id}>
{i.icon}
{i.name}
{i.img}
</li>
)
)}
I have a project that I am developing with React JS. The problem is that I have a button and when I click on it, I just want the icon on the button I click to change. But the icons on all the buttons I click change. My code is below.
constructor(props){
super(props)
this.state={
icon: false
}
}
active = (event) => {
this.setState({icon: !this.state.icon})
}
.....
const menu = ['A','B','C','A','B','C','A','B','C']
<div className="nav_menu">
<ul>
{menu.map((item,index) =>
<li key = {index}>
<Link data-id = {index} className="inactive" to={`${match.url}`} onClick={this.active}>
<span>
<span>
<FontAwesomeIcon icon={faHome} className="icon"/>
</span>
{item}
</span>
<FontAwesomeIcon data-id = {index} icon={icon ? faAngleDown:faAngleRight} className="angle"/>
</Link>
</li>
)}
</ul>
How do I fix this?
Having just one variable wouldn't suffice as you are not storing which button index has been clicked to accurately show the icon on only that button.
constructor(props){
super(props)
this.state={
icon: false,
clickedIndex: -1,
}
}
active = (clickedIndex)=> (event) => {
this.setState(prevState => ({icon: !prevState.icon, clickedIndex }));
}
.....
const menu = ['A','B','C','A','B','C','A','B','C']
<div className="nav_menu">
<ul>
{menu.map((item,index) =>
<li key = {index}>
<Link data-id = {index} className="inactive" to={`${match.url}`} onClick={this.active(index)}>
<span>
<span>
<FontAwesomeIcon icon={faHome} className="icon"/>
</span>
{item}
</span>
<FontAwesomeIcon data-id = {index} icon={(icon && index === this.state.clickedIndex) ? faAngleDown:faAngleRight} className="angle"/>
</Link>
</li>
)}
</ul>
Sample how you can select particular. live demo https://codesandbox.io/s/focused-browser-fls2w
export default class Abc extends Component {
constructor(props) {
super(props);
this.state = { icon: false };
}
active = item => {
this.setState({ icon: item });
};
render() {
const menu = ["A", "B", "C", "A", "B", "C", "A", "B", "C"];
return (
<div className="nav_menu">
<ul>
{menu.map((item, index) => (
<li
key={index}
onClick={() => this.active(index)}
style={{ color: this.state.icon === index ? "red" : "" }}
>
{item}
</li>
))}
</ul>
</div>
);
}
}
I have a need, in a site I'm building, for a list component that is reused several times. However, the list is purely for rendering and is not responsible for the state of the app at all. I know you either cannot, or are not supposed to have dumb components containing any logic, but I am not sure how to proceed without using a smart component, which is entirely unnecessary. Here is my smart component that works:
class Menu extends Component {
renderItems(items) {
return this.props.items.map((i, index) => {
return (
<li key={index} style={{marginLeft: 10}}>
{i}
</li>
)
});
}
render() {
const { listStyle } = styles;
return (
<div>
<ul style={listStyle}>
{this.renderItems()}
</ul>
</div>
)
}
}
And I've tried this:
function Menu(props) {
return props.items.map((i, index) => {
<li key={index} style={{marginLeft: 10}}>
{i}
</li>
});
}
And then calling it inside Nav like this, which does not throw an error but does not render anything from menu either:
const Nav = () => {
const { listStyle, containerStyle } = styles;
return (
<div style={containerStyle}>
<Logo url={'#'}
src={PickAPlayLogo}
width={300} />
<Menu items={pageLinks} />
<Menu items={socialMediaLinks} />
<Logo url={'#'}
src={AppStoreLogo}
width={170} />
</div>
);
};
Also, worth noting, I have never come across a function that is supposed to be rendered like a component, but was trying it based on the example on this page
Heres an answer similar to what you have going on
function Menu(props) {
this.renderItems = () => {
return (
<ul>
{props.items.map((i, index) => {
return (
<li>{i}</li>
)
})}
</ul
)
}
return(
this.renderItems()
)
}
Here we go:
function Menu(props) {
const {listStyle} = styles;
const listItems = props.items.map((i, index) =>
<li key={index} style={{marginLeft: 10}}>
{i}
</li>
);
return (
<ul style={listStyle}>{listItems}</ul>
);
}