Apply 'selected' class to first element in React component on page load - javascript

I have a 'RadioItem' component which applys a 'selected' class via an 'isChecked' boolean prop when it's clicked:
{categoryFilter.map((item) => {
return (
<RadioItem
isChecked={selectedItem.id === item.id}
key={item.id}
itemName={item.name}
itemPrice={item.price}
onClick={() => clickHandler(item)}
/>
);
})}
const RadioItem = (props) => {
const clickHandler = () => {
props.onClick();
};
return (
<li
className={props.isChecked ? "item selected" : "item"}
onClick={clickHandler}
>
<div className="item__body">
<div className="item__radio"></div>
<h3 className="item__name">{props.itemName}</h3>
</div>
{props.itemPrice !== 0 && (
<p className="item__price">£{props.itemPrice}</p>
)}
</li>
);
};
I'd like to apply this 'selected' class to the first item (i.e. where the index is 0) when the page first loads but for it to then be deselected when another item is clicked. I have tried, for example, applying an OR condition to the isChecked, like this:
isChecked={selectedItem.id === item.id || index === 0}
but then of course the selected class persists on that first item regardless of me clicking other items.
Any help greatly appreciated, thanks.

Related

Whenever I click on complete task which filters the item out, the icon passes from the item that was completed to the next item. How do i prevent this

I have it to where the priority element when clicked changes the star color to gold to show it is a priority element.
When I complete the task, the gold(priority star) passes to the following item.
If I try to delete the div element for the completed item, the whole app goes blank.
I would appreciate it if you could point out what I may be missing or what I can do to prevent this.
Function code:
const completeTask = (event) => {
let task = event.target;
// // Mark item as regular again if priority
// task.nextElementSibling.style.color = "black";
changeCompletedList([
...toDo.filter((item) => item.id === task.previousElementSibling.id),
...completedList,
]);
changeToDo(
toDo.filter((item) => item.id !== task.previousElementSibling.id)
);
};
Mapped list:
<ul className="task-list">
{toDo.map((item) => (
<div className="list-items">
<FontAwesomeIcon
className="remove"
icon={faX}
onClick={deleteTask}
/>
<li
id={item.id}
// onClick={toggleSubTask}
>
{item.name}
</li>
<FontAwesomeIcon
className="check"
icon={faCheck}
onClick={completeTask}
/>
<FontAwesomeIcon
className="priority"
icon={faStar}
onClick={markPriority}
/>
</div>
))}
</ul>

How to make certain event happen on a specific item when mapping through an array?

I am mapping through an array and displaying its data. I have edit button to modify that data. When I click edit button, dropdown shows and I am able to edit as shown in screenshot I shared. Open this screenshot
Problem is all of edit buttons work even when I click any one, I want only clicked edit button to work rather than all. How can I achieve this functionality?
This is my code:
const [show, setShow] = useState(false);
const handleShow = () => { setShow(!show); };
<p onClick={handleShow}>
Edit
{show === true ? (
<IoIosArrowUp style={{ color: "#F1BB0F" }} />
) : (
<IoIosArrowDown style={{ color: "#F1BB0F" }} />
)}
</p>
{show && (
<div>
<p>
Unlocks: {lockParams[index]?.endDateString}
</p>
<p>
Unlocker : <span>{lockParams[index]?.unlocker}</span>
</p>
</div>
)}
If you have N different buttons, and you want the view to be different depending on which of those buttons are toggled on or off, you should have state corresponding to those N buttons. An array of booleans would make sense here. That way, if, say, the 4th button is toggled, you can update the 4th element in the state array, and then when the component renders, while iterating over the 4th index, it can look at that 4th boolean to see what should be shown there.
You haven't shown the necessary code in the question, but, for example, if you have:
someArray.map((item) => (
// some JSX
<p onClick={handleShow}>
// etc
Then you need to initialize a state array containing the same number of elements as someArray, like this:
const [shows, setShows] = useState(() => someArray.map(() => false));
const handleShow = (i) => setShows((show, j) => j === i ? !show : show);
And then use index when rendering:
someArray.map((item, i) => (
// some JSX
<p onClick={() => handleShow(i)}>
Edit
{shows[i] ? (
<IoIosArrowUp style={{ color: "#F1BB0F" }} />
) : (
<IoIosArrowDown style={{ color: "#F1BB0F" }} />
)}
</p>
{shows[i] && (
<div>
<p>
Unlocks: {lockParams[index]?.endDateString}

How to close accordion on second click?

I'm having accordion component which is revealed on click. I have an issue with closing it on second click. Currently I'm clicking once and it expands but after second click nothing changes. Thank you for help
const [activeTab, setActiveTab] = React.useState(props.tabIndex);
const tabs = props.tabs.map((tab) => {
return (
<>
<TabContainer>
<Tab
key={tab.index}
onClick={() => setActiveTab(tab.index)}. //here it opens
className={typeof tab.content === 'string' ? '' : 'unactive-tab'}
>
{tab.name}
</Tab>
</TabContainer>
{tab.index === activeTab ?
<div
id="content"
style={{ width: '100%', margin: '2rem 0' }}
dangerouslySetInnerHTML={{ __html: tab.content as string }}
/>
: null}
</>
);
});
Maybe try changing your onClick method to this
onClick= {() => {
activTab == tab.index ? setActiveTab(-1) : setActiveTab(tab.index)
})
In the case where active tab matches the current index of the tab clicked, we set it to -1, hence matching no tab ( showing no tab ); else we set the active tab to the current tab clicked.

Changing order of list elements

I´d like to know, how can I change the order of elements in some list.
For example I would have this list below and buttons for changing order:
<button onClick={"function for moving the item up"}>Move Up</button>
<button onClick={"function for moving the item down"}>Move Down</button>
<ul>
<li>Item1</li>
<li className="selected-item">Item2</li>
<li>Item3</li>
<li>Item4</li>
</ul>
So if I would click on button "Move Up", I want item with class name "selected-item" to move up (change the order position). And exactly the same case for clicking on button "Move Down", but of course the item would move down.
Thank you.
I know a crude way of doing things.
First, make sure your elements are in a list.
Then, allow selecting a single element.
Only show the Move buttons if there's a selection.
Update the index of both the current and previous or next element, swapping them.
Due to the state update, the app gets re-rendered in the right way.
Here's a demo.
import React, { Component } from "react";
import "./styles.css";
export default class App extends Component {
state = {
items: ["Milk", "Curd", "Yogurt"],
currentSelection: -1
};
selectHandler = (idx) => {
let currentSelection = -1;
if (this.state.currentSelection !== idx) {
currentSelection = idx;
}
this.setState({
currentSelection
});
};
handleMove = (dir) => {
const { currentSelection } = this.state;
const items = [...this.state.items];
if (dir === "up") {
// Swap the currentSelection value with the previous one.
let a = items[currentSelection];
items[currentSelection] = items[currentSelection - 1];
items[currentSelection - 1] = a;
} else if (dir === "down") {
// Swap the currentSelection value with the next one.
let a = items[currentSelection];
items[currentSelection] = items[currentSelection + 1];
items[currentSelection + 1] = a;
}
this.setState({
items,
currentSelection: -1
});
};
render() {
const { items, currentSelection } = this.state;
return (
<div>
<p>Click to select and again click to deselect.</p>
<ul>
{items.map((item, key) => (
<li
key={key}
className={currentSelection === key ? "active" : undefined}
onClick={() => this.selectHandler(key)}
>
{item}
</li>
))}
</ul>
{currentSelection !== -1 && (
<div>
<p>What do you wanna do?</p>
<button
disabled={currentSelection === 0}
onClick={(e) => {
e.preventDefault();
this.handleMove("up");
}}
>
Move Up
</button>
<br />
<button
disabled={currentSelection === items.length - 1}
onClick={(e) => {
e.preventDefault();
this.handleMove("down");
}}
>
Move Down
</button>
</div>
)}
</div>
);
}
}
Demo: https://uiyq8.csb.app/
Preview

only one active element

I have a redux store where is rooms array stored (fetched from server) and in my component i fetch it from the store, map it and then display elements with value of room['foo'] but my problem is this: when the components are mapped and displayed the value of one that user clicks is going to be sent to server so i store clicked elements value in components local state like this:
...
handleRoomSelection(roomNumber,index,e){
this.setState({
room: roomNumber
})
}
...
{this.props.rooms.map((val,i)=>{
return (
val.reserved === false ? <p className="" key={i} onClick={e => this.handleRoomSelection(val.roomNumber,i,e)}>{val.roomNumber}</p> : null
)
})}
and this works fine, but my problem is that i want to add className "active" to active element (there can only be one active element) it would be easy if there could be many active elements (i would just add e.target.className = "active" after setState) so how can i achieve my aim?
Basically just do what you stated: set the class based on the room number in your state. setState triggers render(), so enrich even though there's nothing active on first render, once you click, render triggers and we can just set that class inside your map:
render() {
return this.props.rooms.map(val => {
if (val.reserved) return null;
return <p
className={ this.state.room === val.roomNumber && `active`}
key={val.roomNubber}
onClick={() => this.handleRoomSelection(val.roomNumber)}
>{val.roomNumber}</p>;
};
}
Note that we've changed the key to something that uniquely identifies the element we're building a <p> for: never use array index as key, because array position does not identify any specific element, it only identifies a position in a list.
Also note that there's no reason to pass more than just the room number in your click handler if the handler itself only cares about the room number.
Compare the room in state to each room's roomNumber for defining the className
{
this.props.rooms.map((val, i) => {
const className = this.state.room === val.roomNumber ? 'active' : '';
return val.reserved === false ? (
<p
className={className}
key={i}
onClick={e => this.handleRoomSelection(val.roomNumber, i, e)}
>
{val.roomNumber}
</p>
) : null;
});
}
Have you tried to use your state to populate className ?
{this.props.rooms.map((val,i)=>{
return (
val.reserved === false
? <p
className={val.roomNumber === this.state.room ? 'active' : ''}
key={i}
onClick={e => this.handleRoomSelection(val.roomNumber,i,e)}>
{val.roomNumber}
</p>
: null
)
})}

Categories