tab menu corresponding button active - javascript

Hello I am making a tab menu right now.
My problem is that I want to give is-active when the button is clicked, but I don't know what value to put in the empty space there.
type Props = {
title: string
index: number
setSelectedTab: (index: number) => void
}
const TabTitle: React.FunctionComponent<Props> = ({ title, setSelectedTab, index }) => {
// The value to be activated. Initial value is `0th button`
const [activeIndex, setActiveIndex] = useState(0);
const onClick = useCallback(() => {
setSelectedTab(index)
}, [setSelectedTab, index])
return (
<li>
<button
key={index}
className={activeIndex === [empty place] ? "is-active" : ""}
onClick={() => onClick()}
>
{title}
</button>
</li>
)
}
console.log(index);
// 0
// 1
// 2
How to use the index value If you click the 0th button, index : 0
If ​​you click the 1st button, index: 1
After making it like this, className={activeIndex === index ? "is-active" : ""} If you put index , it will work normally, but I don't know how to make an index like that.
How can I set the index to give first click 0 second click 1 ​according to the clicked value?

You need to compare the index property to the selectedTab property (which presumably exists on the parent component, since you pass setSelectedTab function to the TabTitle component).
import { useCallback } from "react";
type Props = {
title: string;
index: number;
selectedTab: number; // <-- this should be passed by the parent
setSelectedTab: (index: number) => void;
};
const TabTitle: React.FunctionComponent<Props> = ({
title,
selectedTab,
setSelectedTab,
index
}) => {
const onClick = useCallback(
(index) => {
setSelectedTab(index);
},
[setSelectedTab]
);
return (
<li>
<button
key={index}
className={selectedTab === index ? "is-active" : ""} // it's active if its index is the same as the selected tab
onClick={() => onClick(index)} // <-- so it knows which button was clicked
>
{title}
</button>
</li>
);
};
export default TabTitle;
You can see a simple example on codesandbox.

Related

dynamic Button class does not update after the array change

So I have an array of objects. I iterate through this array and create a button for each object.
When a button is pressed that object of the button pressed has a value "active" that will be set to true. when another button is pressed its "active" value is now true all all the other ones are turned to false.
it looks like this
myarray.map(item =>
<Button
className={item.active? "btn-active" : "btn-disabled"}
onClick={() => setActive(item);
}}
>
{item.active? "Checking..." : "Start"}
</Button>
)
The behavior I expect is when a button is pressed it turns to action, and all the rest remain inactive, when a new button is pressed the new button is now active and all the rest are disabled. only one active button at a time.
However, the issue I am having is when a new button is pressed it turns to active, but the old one does not change class and stays active also even though it "active" property is set to false.
Any idea how can I fix this behavior?
Without a full picture of how you are using state, here is a working example. Another issue I seen is that you are missing a key on your mapped jsx element.
It's possible you are not mutating myarray statefully.
import "./styles.css";
import React from "react";
export default function App() {
const [myarray, setMyarray] = React.useState([
{ id: 1, active: false },
{ id: 2, active: false }
]);
const setActive = (id) => {
setMyarray((prev) =>
prev.map((item) => {
if (item.id === id) {
return { ...item, active: true };
}
return { ...item, active: false };
})
);
};
return (
<div className="App">
{myarray.map((item) => (
<button
key={`button-${item.id}`}
className={item.active ? "btn-active" : "btn-disabled"}
onClick={() => setActive(item.id)}
>
{item.active ? "Checking..." : "Start"}
</button>
))}
</div>
);
}
https://codesandbox.io/s/flamboyant-shirley-i24v0z

React: setState to a single component from multiple mapped instances of the same component

I have a hover state that changes div background color and adds a <p> tag to a mapped component:
const [isHover, setIsHover] = useState(false)
Here is the mapped component where I set the state:
const AddSectionButton = ({
isHover,
setIsHover,
sections,
setSections,
nextSectionId,
setNextSectionId,
sectionTitle,
setSectionTitle,
sectionId,
}) => {
return (
<AddSectionDiv
onMouseEnter={() => {
setIsHover(!isHover);
}}
onMouseLeave={() => {
setIsHover(!isHover);
}}
style={isHover && { backgroundColor: "#A4AAE0" }}
>
{isHover && <p>Add Section</p>}
</AddSectionDiv>
);
};
Whenever I hover to a single mapped component, the rest of the mapped components trigger the hover effect as well.
How do I set the state to only the hovered component and not affect the rest?
I thought about using a key, where as you can see in my mapped component, I passed a sectionId prop which contains the key, but I'm confused as to how I should use it.
You can, and should, use a key or any value/property that uniquely identifies the element being hovered.
In the parent use an initially null isHover state.
const [isHover, setIsHover] = useState(null);
And in the children set or clear the isHover state by their id. And check if the current isHover value matches the current sectionId value.
const AddSectionButton = ({
isHover,
setIsHover,
sections,
setSections,
nextSectionId,
setNextSectionId,
sectionTitle,
setSectionTitle,
sectionId,
}) => {
return (
<AddSectionDiv
onMouseEnter={() => {
setIsHover(sectionId);
}}
onMouseLeave={() => {
setIsHover(null);
}}
style={isHover === sectionId && { backgroundColor: "#A4AAE0" }}
>
{isHover === sectionId && <p>Add Section</p>}
</AddSectionDiv>
);
};
Consider moving/implementing this isHover state internally to each component, the parent component likely doesn't need to concern itself with the hover status of any of its children. Do this and your original logic is fine.
const AddSectionButton = ({
sections,
setSections,
nextSectionId,
setNextSectionId,
sectionTitle,
setSectionTitle,
sectionId,
}) => {
const [isHover, setIsHover] = useState(false);
return (
<AddSectionDiv
onMouseEnter={() => {
setIsHover(true);
}}
onMouseLeave={() => {
setIsHover(false);
}}
style={isHover && { backgroundColor: "#A4AAE0" }}
>
{isHover && <p>Add Section</p>}
</AddSectionDiv>
);
};

I want to change variant or background of the clicked button (onClick) only in reactjs. How can i achieve it?

What i am trying to do it, when a button is clicked, at the onclick it's variant(material ui button should change from outlined to contained) or simply its background should change. (Please do not suggest for the onFocus property because these is another button in another component, which when clicked focus is lost. So onFocus is not a choice for me here). I am atatching my method here, you can change it (because mine is not working anyhow, it's changing state to true indefinitely)
const [clicked, setClicked] = useState(false);
const categoryChangedHandler = (e) => {
setCategory(e);
};
{categories.map((category, index) => {
console.log("catogoried.map called and categories= " + category);
return <Button className="CategoryButton"
variant={clicked ? "contained" : "outlined"}
color="primary"
value={category}
onClick={() => {
categoryChangedHandler(category);
setClicked(true);
}}
style={{ textAlign: 'center' }}
>
{category}
</Button>
})
}
If you want to show a different color base on it's category, you probably want to change the variant base on the state ( whether it's selected ).
Example
const categories = ['apple', 'banana', 'mango']
const App = () => {
const [ selected, setSelected ] = useState([])
const onClick = (value) => {
//this is a toggle to add/remove from selected array
if (selected.indexOf(value) > -1) {
//if exist, remove
setSelected( prev => prev.filter( item => item !== value )
} else {
//add to the selected array
setSelected( prev => [ ...prev, value ] )
}
}
return <div>
{categories.map((category, index) => {
return <Button className="CategoryButton"
/* if the category had been selected, show contained */
variant={ selected.indexOf(category) > -1 ? "contained" : "outlined"}
color="primary"
value={category}
onClick={() => {
onClick(category);
}}
style={{ textAlign: 'center' }}
>
{category}
</Button>
})
}</div>
}
The above example keeps an array of categories selected. OF course, if you only want to allow ONE to be selected at each click, then instead of an array, you can use setSelected(value) (where value is the category name), then in your button component use
variant={ selected === category ? 'contained' : 'outlined' }
Remember to change your use state to use string instead of array
const [ selected, setSelected ] = useState('') //enter a category name if you want it to be selected by default

How to dynamically change className of a component in React?

I want to make a button check which add a new calssName to my list. I use a function to update a state and take the string. If you want to help me be more specific because I am a beginner. Thanks !
const [check, setCheck] = useState({
id: '',
status: false
});
This is the function. With 'on' I take the string to add to id.
let complet = (on) =>{
if(check.status == false){
setCheck({
id: on,
status: true
})
}
else {
setCheck({
id: on,
status: false
})
}
}
And how I Display the list and how I check correspond to the string.
return(
<div className='display'>
{ list.map( (list,index) => (
<div className={ check.status && check.id == list ? 'lista complet' : 'lista'} key= {index} id='lista' >
{list}
<button className='btnCheck' onClick={complet.bind(this, list)}> <FcCheckmark/> </button>
<button className='btnRemove' onClick={remove.bind(null, list)}> <BsTrash/> </button>
</div>
))}
</div>
)
If you want to store the checked ids and the unchecked ids, you must change your state variable because currently it can only stores a single element. However, it seems you are rendering a list of elements that can be checked individually
Here is a possible solution :
function App({list}) {
const [checkIds, setCheckIds] = useState(() => {
const item = localStorage.getItem('checkIds');
return item ? JSON.parse(item) : {};
});
// reset the checkIds when the list is updated
useEffect(() => setCheckIds({}), [list]);
// save the checkIds into the local storage
useEffect(() => {
localStorage.setItem('checkIds', JSON.stringify(checkIds));
}, [checkIds]);
function checkId(id) {
setCheckIds({...checkIds, [id]: true);
}
function uncheckId(id) {
setCheckIds({...checkIds, [id]: false);
}
return (
<div className='display'>
{list.map(id => (
<div key={id} id={id} className={check[id] ? 'lista complet' : 'lista'}>
{id}
<button className='btnCheck' onClick={() => checkId(id)}>
<FcCheckmark/>
</button>
<button className='btnRemove' onClick={() => uncheckId(id)}>
<BsTrash/>
</button>
</div>
))}
</div>
)
}

Apply active state on clicked button only

I have a button that changes the active state onClick:
render() {
return(
<SomeButton
onClick={e => this.handleClick(e)}
id={someId}
activeStatus={someId === this.state.active ? "active" : "not active"}
/>
)
}
The function that changes the state:
handleClick(e) {
e.preventDefault();
this.setState({ active: e.currentTarget.id });
}
The state:
this.state = {
active: null
};
The button that receives the activeStatus props:
export default function SomeButton({ activeStatus }) {
console.log(activeStatus);
return (
// button jsx code
);
}
However, every time I click on the button (I have 3 instances of that button on the page), the activeStatus console.log shows:
I click on button 1:
active
not active
not active
I click on button 2:
active
active
not active
I click on button 3:
active
active
active
I was expecting that the status would toggle depending on the active button which is clicked.
What am I missing?
You can set the state in an array:
this.state = {
active: [false, false, false] // or just: []
};
handleClick(e) {
e.preventDefault();
const activeState = [false, false, false]; // or just: []
activeState[e.currentTarget.index] = true;
// button index ^^
this.setState({ active: activeState });
}
And just pass the activeStatus to the active state:
activeStatus={this.state.active}
Inside your component, bind the active state:
<button className={ activeStatus[0] ? 'active' : 'not-active' }>...</button>
<button className={ activeStatus[1] ? 'active' : 'not-active' }>...</button>
<button className={ activeStatus[2] ? 'active' : 'not-active' }>...</button>
I'd use e.target.id instead of e.currentTarget.id and if the button ids are static, then you could put them into your state and use the id to update a buttonState object (one of several ways to handle it).
Working example: https://codesandbox.io/s/olmn9k08m5
Some notes:
Keep your state consistent (if it's a string, keep it a string, if
it's an array, keep it any array...etc -- in the example below
buttonState is an object and stays an object).
Also, you don't need e.preventDefault() unless you're submitting a
form or trying to block functionality.
Always specify the button's type ("button" or "submit")
ShowButton.js
import React, { Component } from "react";
import SomeButton from "./SomeButton";
const buttons = ["button1", "button2", "button3"];
export default class App extends Component {
state = {
buttonState: {
button1: "inactive",
button2: "inactive",
button3: "inactive"
}
};
handleClick = e => {
const { id } = e.target; // id="button1","button2" or "button3"
this.setState(prevState => ({
buttonState: {
...prevState.buttonState, // spread out object
[id]: prevState.buttonState[id] === "active" ? "inactive" : "active" // use the [id] as an object property (ex: "button1") and set the property's value to "active" or "inactive"
}
}));
};
render = () => (
<div className="container">
<h1>Controlling Active Buttons</h1>
{buttons.map(id => (
<SomeButton
key={id}
id={id}
handleClick={this.handleClick}
activeStatus={this.state.buttonState[id]}
/>
))}
</div>
);
}
SomeButton.js
import React from "react";
export default ({ activeStatus, handleClick, id }) => (
<div style={{ marginBottom: 20 }}>
<button
type="button"
style={{ minWidth: 150 }}
className={`uk-button ${
activeStatus === "active" ? "uk-button-primary" : null
}`}
id={id}
onClick={handleClick}
>
{activeStatus}
</button>
</div>
);

Categories