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>
);
Related
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
fairly new to react, so sorry in advanced. I have a need where I need to uncheck any checked checkboxes if one of them are checked. This component is a grandchild component of my app.js
App.js:
class App extends Component {
constructor (props) {
super (props);
this.state = {
data:[
{
id:'player1',
toggleId: 'audio0',
checked:false,
isAlt:false
},
{
id:'player2',
toggleId: 'audio1',
checked:false,
isAlt: true
},
{
id:'player3',
toggleId: 'audio2',
checked:false,
isAlt:false
},
{
id:'player4',
toggleId: 'audio3',
checked:false,
isAlt: true
}
]
}
}
render(){
return (
<div className="App">
<Notice/>
{this.state.data.map((dynamicComponent, i) =>
<AudioPlayer key = {i} componentData = {dynamicComponent}/>)
}
{/* {audioMap.map(i => {
return <AudioPlayer toggleId={this.state.toggleId[i]} checked={this.state.checked[i]}/>
})} */}
</div>
);
}
}
export default App;
In the audio player component, I have a state for active and setActive and am rendering a toggle switch child component:
AudioPlayer.js:
const [checked, setChecked] = useState(props.componentData.checked);
{/*toggle switch */}
<div className="toggleSwitch">
<ToggleSwitch id={props.componentData.toggleId} checked={checked} onChange={checked => setChecked(checked)} />
</div>
ToggleSwitch.js (taken from https://www.sitepoint.com/react-toggle-switch-reusable-component/):
const ToggleSwitch = ({ id, name, checked, onChange, optionLabels, small, disabled }) => {
function handleKeyPress(e){
if (e.keyCode !== 32) return;
e.preventDefault();
onChange(!checked)
console.log('clicked');
}
return (
<div className={"toggle-switch" + (small ? " small-switch" : "")}>
<input
type="checkbox"
name={name}
className="toggle-switch-checkbox"
id={id}
checked={checked}
onChange={e => onChange(e.target.checked)}
disabled={disabled}
/>
{id ? (
<label className="toggle-switch-label"
htmlFor={id}
tabIndex={ disabled ? -1 : 1 }
onKeyDown={ (e) => { handleKeyPress(e) }}>
<span
className={
disabled
? "toggle-switch-inner toggle-switch-disabled"
: "toggle-switch-inner"
}
data-yes={optionLabels[0]}
data-no={optionLabels[1]}
tabIndex={-1}
/>
<span
className={
disabled
? "toggle-switch-switch toggle-switch-disabled"
: "toggle-switch-switch"
}
tabIndex={-1}
/>
</label>
) : null}
</div>
);
}
// Set optionLabels for rendering.
ToggleSwitch.defaultProps = {
optionLabels: ["On", "Off"],
};
ToggleSwitch.propTypes = {
id: PropTypes.string.isRequired,
checked: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
name: PropTypes.string,
optionLabels: PropTypes.array,
small: PropTypes.bool,
disabled: PropTypes.bool
};
export default ToggleSwitch;
As you can see, I'm rendering four of the same child component, and each of them have a nested toggleSwitch/checkbox. The app.js is setting the checked to false onLoad. I need to make it such that I need to uncheck any active checkbox if one gets checked.If one is checked, then uncheck any active ones. From what I understand, I need to pass data from the child to the parent, but I'm not sure how to do that. Can I even affect the state in the parent? From what I understand, I shouldn't be using props to set the state data. Any help would be appreciated. Thank you
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.
i have this breadcrump component that map over props and renders a list of chip components like this:
class BreadCrumb extends React.Component {
render () {
const {
steps,
activeIndex
} = this.props;
const chips = steps
.map((step,index) => {
return <Chip
key={index}
title={step.category}
onClick = {()=> this.props.selectChip(index)} // this should be passed only if
// active == true
active={activeIndex >= index} />
})
return (
<div className="chip-container">
{chips}
</div>
)
}
}
i need to click on chips only if his active prop is true,
this is the chip component
class Chip extends React.Component {
render(){
const {
active,
title
} = this.props;
const activeClassName = active ? 'chip active' : 'chip';
return (
<div
className = {activeClassName}
onClick = {() => this.props.onClick()} >
<span>{title}</span>
</div>
)
}
}
how can i make chip clickable only if the active prop is true?
For further information selectChip() function sets the state of a component App, parent of Breadcrump component, so it is binded to App component.
You could e.g. make that onClick function as a class method and use a simple condition inside:
class Chip extends React.Component {
handleClick = () => {
if (this.props.active) {
this.props.onClick(); // call only if active props is true
}
}
render() {
const { active, title } = this.props;
const activeClassName = active ? 'chip active' : 'chip';
return (
<div
className = {activeClassName}
onClick = {this.handleClick}
>
<span>{title}</span>
</div>
)
}
}
Either execute the handler or an empty function
onClick = {isActive ? this.props.onClick : () =>{} } >
You can do it like this:-
// If chip component expects a function all the time
<Chip
key={index}
title={step.category}
onClick = {step.active ? ()=> this.props.selectChip(index) : () => {}}
active={activeIndex >= index} />
// If onClick is an optional prop to chip component
<Chip
key={index}
title={step.category}
onClick = {step.active ? ()=> this.props.selectChip(index) : undefined}
active={activeIndex >= index} />
// of onClick handler is optional, possibly an alternative solution
type ChipProps = {
title: string;
active: boolean;
onClick?: ()=>void;
}
<Chip
key={index}
title={step.category}
active={activeIndex >= index}
{...(step.active ? {onClick:()=> this.props.selectChip(index)} : {})}
/>
i m learning react and i trying to change my button text after sweetAlert2-react popup.
The original text on the button is 'ACTIVE' and
If i choose OK on the popup the text on the button must be 'activated' and if y choose CANCEL 'disabled'..
I do not know how or where to do the IF iteration
any help?
here is my code:
<button onClick={() => this.setState({ show: true })}>ACTIVE</button> <SweetAlert show={this.state.show} showCancelButton={this.state.show} title="Are you shore?" onConfirm={() => this.setState({ show: false })} />
Thanks!
Conditionally set the string internal to the button.
<button>
{this.state.active === 'active' && <div>ok</div>}
{this.state.active === 'disabled' && <div>idk</div>}
<button>
If there are only two states consider a ternary operator.
<button>
{this.state.active === 'active' ? <div>ok</div> : <div>idk</div>}
<button>
You could save the button's text in the state, (ex: this.state.buttonText), then you'll be able to set the buttonText when user click on OK/Cancel button.
You can use a common method (in the below example hideAlert) for manage both events (ok and cancel) and inside it, set the button's text.
See the following example please (click here to run):
import React, { Component } from "react";
import SweetAlert from "react-bootstrap-sweetalert";
import ReactDOM from "react-dom";
export default class HelloWorld extends Component {
constructor(props) {
super(props);
this.state = {
alert: null,
button: "active"
};
}
showAlert() {
const getAlert = () => (
<SweetAlert
show={this.state.show}
showCancel
onConfirm={() => this.hideAlert("disabled")}
onCancel={() => this.hideAlert("active")}
>
Are you sure?
</SweetAlert>
);
this.setState({
alert: getAlert()
});
}
hideAlert(text) {
this.setState({
alert: null,
button: text
});
}
render() {
return (
<div style={{ padding: "20px" }}>
<button onClick={() => this.showAlert()}>
{this.state.button}
</button>
{this.state.alert}
</div>
);
}
}
ReactDOM.render(<HelloWorld />, document.getElementById("app"));
I hope it helps you, bye.