How to add class to clicked element only in reactjs? - javascript

I have dynamic menu items created from api. I'm calling class change function onClick but class is adding to all menus. I need add class to only clicked element not to whole menu list below is my code.
{menuSideBar.map(menu => (
<ListItem
className={SideMenuClass.join(" ")}
onClick={this.toggleClass.bind(this)}
button
>
<ListItemIcon className="sidebar_menu_icons">
<SendIcon />
</ListItemIcon>
<Link className="sidebar_menu_links" to={menu.url != "" ? "/" + menu.url : "#"}>
<ListItemText inset primary={menu.resourceValue} />
</Link>
</ListItem>
))}
Below is function
toggleClass = () => {
this.setState({ addClass: !this.state.addClass });
};
let SideMenuClass = ["sidebar_menu_list"];
if (this.state.addClass) {
SideMenuClass.push("selected_sidebar_menu");
}
Please help how can I add class to only clicked element?
Thank you.

You can save id's of selected items as array in state
{menuSideBar.map(menu => (
<Item
className={this.state.ids.includes(menu.id) ? 'active' : ''}
onClick={() => {
this.setState(oldState => ({
ids: oldState.ids.includes(menu.id) ? oldState.ids.filter(x => x !== menu.id) : [...oldState.ids, menu.id]
}));
}}
button
/>
))}
Example: https://codesandbox.io/s/4wv6n31q90?fontsize=14
Or if you need only last one selected:
{menuSideBar.map(menu => (
<Item
className={this.state.lastClicked === menu.id ? 'active' : ''}
onClick={() => {
this.setState({lastClicled: menu.id});
}}
button
/>
))}

Store menu item id in the state - then in render (map) check if equals to current element - adjust classes acordingly (apply class for selected only).

Related

hiding only the selected component

hides only the selected component. I am unable to hide only a specific component
useEffect(() => { result() }, []);
const dataComponent = state.data.map((video, index) => {
return <>
<p onClick={() => showMoreInfo(!showing)}>show</p>
{showing
? <ContainerFilmHome key={index} name={video.show_name} pictures={video.pictures}/>
: null}
</>
})
You need update showing to store index of item selected:
onClick={() => showMoreInfo(index === showing ? null : index)}
{showing === index
? <ContainerFilmHome key={index} name={video.show_name} pictures={video.pictures}/>
: null}

React recursion - can't get correct value

I'm having Array of Objects named items. Each Object looks like this
interface IItem {
id: string;
title: string;
items: IItem[] | null
}
So I'm doing recursion, as in
parent component:
{items?.map((folder, index) => (
<Item
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
console.log(
event.target.innerHTML,
folder.id,
folder.title
);
}}
key={folder.id}
data={folder}
/>
))}
WhileItem component looks like this:
const nestedItems =
data.items
? data.items.map((item, index) => (
<Item
onClick={onClick}
key={item.id}
data={item}
/>
))
: null;
return (
<div>
<button onClick={onClick}>
<Typography>
{data.title} {data.id}
</Typography>
</button>
<div>{nestedItems}</div>
</div>
Html output and whats rendered in browser looks correct,
but when I try to get {id}, or {title} from item in parent component
the output of any click which has parent doesn't log it's own id, but it's parent id.
The strange thing is that console.log from above on any "not first level items" looks like this:
event.target.innerHTML - correct value
folder.id, folder.title - has parent ? parent id value : it's own id value
So I'm not really sure what's happening and where is the catch :/
Thanks in advance!
You only define onClick for each of the root-level Items and then within the Item component you just pass the onClick prop to the child. Thus every child has the same onClick function as its parent.
To fix this you can change onClick to be more generic by accepting data (the id and/or other information) rather than having data curried into it. This way the onClick function passed to the Item works for any item, but the onClick function passed to the button within each Item is unique to that item.
For example:
Parent component:
{items?.map((folder, index) => (
<Item
onClick={(event, clickedFolder) => {
event.stopPropagation();
event.preventDefault();
console.log(
event.target.innerHTML,
clickedFolder.id,
clickedFolder.title
);
}}
key={folder.id}
data={folder}
/>
))}
Item component:
const nestedItems =
data.items
? data.items.map((item, index) => (
<Item
onClick={onClick}
key={item.id}
data={item}
/>
))
: null;
return (
<div>
<button onClick={event => onClick(event, data)}>
<Typography>
{data.title} {data.id}
</Typography>
</button>
<div>{nestedItems}</div>
</div>
)

React class prop onclick

This is my scenario
<List>
{mainTags.map((mainTagItem) => {
return (
<ListItem onClick={() => { setMainTag(mainTagItem.tag.tagId) }} button className={classes.mainTagItem}>
<div className={classes.mainTagCircle}></div>
<ListItemText
primary={mainTagItem.tag.name}
/>
</ListItem>
)
})}
</List>
when i click on my ListItem ( that becomes selected ) i want the element <div className={classes.mainTagCircle}> has an active class
For Example:
<div classes={{ root: !!listItemSelected ? classes.mainTagCircleActive : classes.mainTagCircle, }}></div>
I have already a method onClick in my ListItem, how can i apply this logic?
given that you have a mainTag state you could compare with your element tagId to define which class to select. If it's the same as your state then active class wil be returned:
<div className={
mainTag === mainTagItem.tag.tagId
? classes.mainTagCircleActive
: classes.mainTagCircle}>
</div>
Solution with a library
You could try with this library clsx. Then do something like this:
function Component() {
const [mainTag, setMainTag] = useState(null);
return (
<List>
{mainTags.map((mainTagItem) => {
return (
<ListItem onClick={() => { setMainTag(mainTagItem.tag.tagId) }} className=
{clsx([classes.mainTagItem, mainTag === mainTagItem.tag.tagId ? :
'activeClass': 'defaultClass' ])}>
<div className={classes.mainTagCircle}></div>
<ListItemText
primary={mainTagItem.tag.name}
/>
</ListItem>
)
})}
</List>
)
}
Solution without libraries
function Component() {
const [mainTag, setMainTag] = useState(null);
return (
<List>
{mainTags.map((mainTagItem) => {
return (
<ListItem onClick={() => { setMainTag(mainTagItem.tag.tagId) }} className={
mainTag === mainTagItem.tag.tagId
? classes.mainTagCircleActive
: classes.mainTagCircle}
/>
</ListItem>
)
})}
</List>
)
}

Highlight One Item in a List at any given time (Index 0 by default)

Sorry for the horrible title, I couldn't for the life of me figure out how to word this problem. By default I want the first item in my list to be highlighted and others to be highlighted when the mouse is over them. This works fine and I was curious how I am able to remove the highlight of that initial item when another is highlighted, I know I need to handle the state within my parent component (List) but I cannot figure it out.
This is my list item that contains some basic state management to show whether the given item is highlighted or not.
export default function Item ({ title, onClick, selected, i }) {
const [highlight, setHighlight] = useState(false)
return (
<ListItem
i={i}
key={title}
selected={selected}
onClick={onClick}
onMouseEnter={() => setHighlight(true)}
onMouseLeave={() => setHighlight(false)}
// i === 0 && no other item is highlighted (need to get this state from the list component)
highlight={highlight || i === 0}
>
{title}
</ListItem>
)
}
Parent component that takes in a 'list' and maps each item in that list to the above component (Item):
export default function List ({ list, onClick, selected, render }) {s
return (
<div>
{render ? (
<ListContainer>
{list.map((item, i) => (
<Item
i={i}
key={item.title}
title={item.title}
onClick={() => onClick(item.title)}
selected={selected(item.title)}
/>
))}
</ListContainer>
) : null}
</div>
)
}
Here is a Gyazo link that shows the current implementation, what I want to achieve is for that initial item to no longer be highlighted when the mouse has entered another item where the index does not equal 0.
You need to lift your highlight state to parent List component
function List ({ list, onClick, selected, render }) {
const [highlightIndex, setHighlightIndex] = setState(0);
return (
<div>
{render ? (
<ListContainer>
{list.map((item, i) => (
<Item
i={i}
key={item.title}
title={item.title}
onClick={() => onClick(item.title)}
selected={selected(item.title)}
highlightIndex={highlightIndex}
setHighlightIndex={setHighlightIndex}
/>
))}
</ListContainer>
) : null}
</div>
)
}
function Item ({ title, onClick, selected, i, highlightIndex, setHighlightIndex }) {
return (
<ListItem
i={i}
key={title}
selected={selected}
onClick={onClick}
onMouseEnter={() => setHighlightIndex(i)}
highlight={i === highlightIndex}
>
{title}
</ListItem>
)
}
you can do it with css
ul:not(:hover) li:first-of-type,
ul li:hover {
background-color: red;
}
the first one will be highlighted while not hovered, and the other will be highlighted when hovered and "cancel" the first one.
Presuming the selected one will go to the top of the list.

Match Q&A with React?

I have a set of questions and a set of answers. Each answer is the only correct answer for one question. I need to highlight the right selected question or answer when it's clicked.
For example:
When a question is clicked, change that specific question's class to "active" (so the css changes)
When an answer is clicked, change that specific answer's class to "active"
Here's the main page:
constructor(props) {
super(props)
this.handleQAClick = this.handleQAClick.bind(this)
this.toggleColour = this.toggleColour.bind(this)
this.state = {
questions: [],
active: true
}
}
...
handleQAClick = (type, id) => {
console.log(id)
console.log(type)
this.toggleColour(id)
}
toggleColour = id => {
this.setState({active: !this.state.active})
console.log('should change colour')
}
...
<Card>
{this.state.questions.length
? (
<List>
{this.state.questions.map(question => (
<ListItem key={question._id}>
<MatchItem
id={question._id}
type="question"
active={this.state.active}
text={question.question}
handleClick={this.handleQAClick}
/>
</ListItem>
))}
</List>
)
: ('No questions found')
}
</Card>
<Card>
{this.state.questions.length
? (
<List>
{this.state.questions.map(question => (
<ListItem key={question._id}>
<MatchItem
id={question._id}
type="answer"
text={question.option1}
handleClick={this.handleQAClick}
/>
</ListItem>
))}
</List>
)
: ('No questions found')
}
</Card>
Here's the MatchItem component:
import React, {Component} from 'react'
export class MatchItem extends Component {
render() {
return (
<div className={`match-item${this.props.active ? "-active" : ""}`} data-id={this.props.id} data-type={this.props.type} onClick={() => this.props.handleClick(this.props.id, this.props.type)}>
{this.props.text}
</div>
)
}
}
One way to approach this can be:
Assign a unique id to each Card (Question).
Change classes this way
<Component
className={this.state.currQuestion === question.id ? 'active' : null}
onClick={this.handleClick(question.id)}
/>
And you can manage states this way:
handleClick = (id) => {
this.setState({
currQuestion: id
})
}

Categories