Change Component property through onClick - reactjs

I have a ButtonGroup with a few Buttons in it, and when one of the buttons gets clicked, I want to change its color, I kinda want to make them behave like radio buttons:
<ButtonGroup>
<Button
variant={"info"}
onClick={(e) => {
..otherFunctions..
handleClick(e);
}}
>
<img src={square} alt={".."} />
</Button>
</ButtonGroup>
function handleClick(e) {
console.log(e.variant);
}
But that doesnt work, e.variant is undefined.
If it was just a single button I would have used useState and I would be able to make this work, but how do I make it work when there are multiple buttons, how do I know which button is clicked and change the variant prop of that button? And then revert the other buttons to variant="info"
Another approach that I could think of is to create my own Button that wraps the bootstrap Button and that way I can have access to the inner state and use onClick inside to control each buttons state, but I'm not sure if that will work, as then how would I restore the other buttons that werent clicked..?

To further from my comment above, you could create your own button component to handle its own state and remove the need to have lots of state variables in your main component e.g.
const ColourButton = ({ children }) => {
const [colour, setColour] = React.useState(true)
return (
<button
onClick={ () => setColour(!colour) }
style = {{color: colour ? "red" : "blue"} }
>
{ children }
</button>
)
}
That way you can just wrap your image in your new ColourButton:
<ColourButton><img src={square} alt={".."} /></ColourButton>
Edit:
I actually like to use styled-components and pass a prop to them rather than change the style prop directly. e.g. https://styled-components.com/docs/basics#adapting-based-on-props

EDIT: Kitson response is a good way to handle your buttons state locally :)
I like to handle the generation of multiple elements with a function. It allows me to customize handleClick.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [buttons, setButtons] = useState([
{
id: 1,
variant: "info"
},
{
id: 2,
variant: "alert"
}
]);
const handleClick = id => {
setButtons(previous_buttons => {
return previous_buttons.map(b => {
if (b.id !== id) return b;
return {
id,
variant: "other color"
};
});
});
};
const generateButtons = () => {
return buttons.map(button => {
return (
<button key={button.id} onClick={() => handleClick(button.id)}>
Hey {button.id} - {button.variant}
</button>
);
});
};
return <div>{generateButtons()}</div>;
}
https://jrjvv.csb.app/

You can maintain a state variable for your selected button.
export default class ButtonGroup extends Component {
constructor(props) {
super(props);
this.state = {
selected: null
};
}
handleClick = e => {
this.setState({
selected: e.target.name
});
};
render() {
const selected = this.state.selected;
return (
<>
<button
name="1"
style={{ backgroundColor: selected == 1 ? "red" : "blue" }}
onClick={this.handleClick}
/>
<button
name="2"
style={{ backgroundColor: selected == 2 ? "red" : "blue" }}
onClick={this.handleClick}
/>
<button
name="1"
style={{ backgroundColor: selected == 3 ? "red" : "blue" }}
onClick={this.handleClick}
/>
</>
);
}
}
Here is a working demo:
https://codesandbox.io/live/OXm3G

Related

How to update back prop to child componet using react hook

I have a parent componet like this, just to show the dialog
The Child Component ( Main to show dialog)
export const MedicalRecord = memo(function MedicalRecord() {
// const onPressViewAll = useCallback(() => {}, [])
const [show, setShow] = useState(false) ///to show dialog
function hanndleDialog() {
setShow(!show) set to show dialog
}
// useEffect(() => {
// if (show == true) {
// setShow(!show)
// }
// },[show])
return (
<SummaryViewContainer
count={5}
title={"dashboardScreen.medicalRecords.title"}
onPress={() => {
hanndleDialog()
}}
>
<View>
{show && (
<ProgressDialog
show={show} //pass t
callback={() => {
hanndleDialog()
}}
/>
)}
<RecordItem />
<RecordItem />
<RecordItem />
</View>
</SummaryViewContainer>
)
})
And parent componet to show this dialog
export default function DialogTesting(show: boolean, { callback }) {
const [showDialog, doShow] = useState(show) //show to present show in child
return (
<View>
{/* <Button
title="click"
onPress={() => {
setShow(true)
}}
>
<Text>Show dialog</Text>
</Button> */}
<Dialog
visible={showDialog}
title="Record New Progress"
style={DIALOG}
onClose={() => {
doShow(false)
callback()
}}
>
But i cant figure out how to open dialog again when close the dialog, it only open for once, i try React Hook : Send data from child to parent component but not work !
How can i show dialog and when i click close button, the children return orignal state so i can click it again, thank you guy so much
Here is a short video of this problem
https://recordit.co/0yOaiwCJvL
I am assuming that you want to find a way to show hide a component based on click. So this is the sandbox for the same.
In this solution, instead of using a derived state, the state is held in the parent's state and the child is mounted/unmounted based on that state.
The state can be updated by a method present in the parent and this method is passed to the child to be triggered on the "hide child" button. The same method is used to show the child component as well.
Below is the core code for the same,
import React from "react";
const Dialog = ({ hideMe }) => {
return (
<div>
<div>I am dialog</div>
<button onClick={hideMe}>Hide me</button>
</div>
);
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = { showDialog: false };
}
toggleDialog = () => {
this.setState((prevState) => {
return { showDialog: !prevState.showDialog };
});
};
render() {
return (
<div>
<div>I am parent.</div>
<button onClick={this.toggleDialog}>Toggle Dialog</button>
{this.state.showDialog ? <Dialog hideMe={this.toggleDialog} /> : null}
</div>
);
}
}
export default App;

passing a paremeter in a consumer to change context value

I'm having a great difficulty with this for a few days. How does one change the context value in a nested component inside a provider wrapper.
For example:
The provider:
<MapThemeContext.Provider value={this.state}>
<div className="App">
<Dashboard/>
</div>
</MapThemeContext.Provider>
where this.state (within the same component as the where the provider is rendered)
this.state = {
theme : themes.silver,
handleMapChange: this.handleMapChange,
}
this.handleMapChange = (style) => {
if (style == "silver") {
this.setState({
theme : themes.silver
});
} else if (style == "dark") {
this.setState({
theme : themes.dark
})
}
}
}
And deep inside the provider (nested insside the Dashboard) is a consumer:
<MapThemeContext.Consumer>
{({handleMapChange}) => (
<div>
<Button variant="contained" className={classes.button} onClick={e => handleMapChange('silver')}> // this does not work
Silver
</Button>
<Button variant="contained" className={classes.button} onClick={handleMapChange}>
Dark
</Button>
</div>
)}
</MapThemeContext.Consumer>
How does the method call within the consumer use the correct parameter to set the value of the context?
Someone again deep down is another component that uses this context for the theme, and it does use the default value correctly. I just don't understand how to change it on the consumer button.
The context content:
export const themes = {
silver: {
foreground: '#000000',
background: '#eeeeee',
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
};
export const ThemeContext = React.createContext(
theme: themes.silver,
handleMapChange: () => {},
);
Can you replace below two section and check once.
----------------------------- Section 1 -----------------------------
const isTheme = this.state.theme;
let buttonName = "dark";
if(isTheme == "silver"){
buttonName = "silver";
}
<MapThemeContext.Consumer>
<div>
<Button variant="contained" className={classes.button} onClick={handleMapChange}>{buttonName}</Button>
</div>
</MapThemeContext.Consumer>
----------------------------- Section 2 -----------------------------
this.handleMapChange = () => {
const style = this.state.theme;
if (style == "silver") {
this.setState({
theme : themes.dark
});
} else if (style == "dark") {
this.setState({
theme : themes.silver
})
}
}
}

How to set color of only the clicked IconButton - ReactJS?

I am new to React JS. I have multiple IconButtons. OnClick I want only the clicked button to change its color. I have used state but when state changes, the color of all buttons change. What approach should I apply? Is there a way to change color without using state? Is there need of a key or id?
The code I have provided is cropped (means it only contains parts I think are relevant).
class Utilitybar extends React.Component {
constructor(props) {
super(props);
this.state = {
bgColor: "default"
};
}
render() {
return (
<div>
<IconButton color={
this.state.bgColor
}
onClick={
() => {
this.props.vidToggle();
if (this.state.bgColor === "default") {
this.setState({ bgColor: "primary" })
} else {
this.setState({ bgColor: "default" })
}
}
}>
<FaPlayCircle />
</IconButton>
<IconButton color={
this.state.bgColor
}
onClick={
() => {
this.props.fileToggle();
if (this.state.bgColor === "default") {
this.setState({ bgColor: "primary" })
} else {
this.setState({ bgColor: "default" })
}
}
}>
<FaRegFileAlt />
</IconButton>
</div>
);
}
}
I expect only clicked button to change color. But obviously they all use the same state, and when state is changed then the color of all buttons is changed.
Instead of storing a shared property (bg color), store the information you really need : i.e. the pressed button
class Utilitybar extends React.Component {
constructor(props) {
super(props)
this.onButtonClicked = this.onButtonClicked.bind(this)
this.state = { currentButton: null }
}
onButtonClicked (id) {
this.setState({ currentButton: this.state.currentButton === id ? null : id })
}
render(){
return (
<div>
<IconButton
color={this.state.currentButton === 0 ? "primary" : "default" }
onClick={() => this.onButtonClicked(0)}>
<FaPlayCircle/>
</IconButton>
<IconButton
color={this.state.currentButton === 1 ? "primary" : "default" }
onClick={() => this.onButtonClicked(1)}>
<FaRegFileAlt/>
</IconButton>
</div>
);
}
}
edit: the idea is to store an ID corresponding to one of your buttons (note that I assumed only one button can be clicked at a time). This ID is put in the component state. Then each button will check its ID against the ID in the state; if it match then it will render a different background.
Since you probable want the button to be unpressed after a second click, the onButtonClicked checks the current ID before updating the state, and if it is the same as the new one, it clears the stored ID instead

Implement dynamic propTypes in ReactJs component

I have an object with icons available to some button.
const icons = {
check: 'icon-CheckSmall',
chat: 'icon-ChatMedium',
investors: 'icon-InvestorsMedium',
download: 'icon-DownloadMedium',
};
const Button = (props) => {
const {
buttonType,
buttonText,
onClick,
disabled,
} = props;
return (
<button
style={ icons }
type={ buttonType }
onClick={ onClick }
disabled={ disabled && 'disabled' }
>
{ <FormattedMessage id={ buttonText } /> }
</button>
);
};
I want to create prop types for component with keys array
Button.propTypes = {
icon: PropTypes.oneOf(Object.keys(icons)),
};
In this case, Object.keys does not work.
Is there anyone who manage to implement dynamic prop types in the component?

Props are undefined in React

I'm new to js and reactjs. I'm trying to create a ButtonGroup with few Buttons inside, in hope of when I click a particular Button (in ButtonGroup) only that particular button will get highlighted (change colour) and rest will be of normal colour.
Below is the code which does the above behaviour but in setColour method I'm getting an error _this.state.selected.props is undefined. Could someone point out the where I'm getting wrong ? Also, if someone can tell me if this is the correct way to approach this problem.
import React from "react"
import {
ButtonGroup,
Button
} from "reactstrap"
class MainButtonsGroup extends React.Component {
constructor (props) {
super(props)
this.state = {
selected: null
}
}
handleSelection = (e) => {
this.setState({selected: e.target})
}
setColour = (key) => {
if (this.state.selected)
{
// ERROR : _this.state.selected.props is undefined
return (this.state.selected.props.key === key) ? 'primary' : 'secondary'
}
}
render() {
return (
<ButtonGroup>
<Button key={1} onClick={this.handleSelection} color={this.setColour(1)}>MainButtonA</Button>
<Button key={2} onClick={this.handleSelection} color={this.setColour(2)}>MainButtonB</Button>
<Button key={3} onClick={this.handleSelection} color={this.setColour(3)}>MainButtonC</Button>
</ButtonGroup>
)
}
}
export default MainButtonsGroup;
You should not hold on to the e.target reference and you must be getting a React warning in your console about it? You just created a memory leak in your app.
Instead copy what you need from the target and let the reference be garbage collected. Although in your case there's no need to be attaching data to the DOM node:
<Button onClick={() => this.handleSelection(this.setColour(3))}>MainButtonC</Button>
Note you don't need key={3} unless you're mapping the elements in a loop.
handleSelection = (color) => {
this.setState({ selected: color })
}
However this code is a bit strange, just record the index of the clicked button and give it a class to style it e.g.
class MainButtonsGroup extends React.Component {
state = {
selectedIndex: null,
}
handleSelection = (index) => {
this.setState({selectedIndex: index})
}
render() {
const idx = this.state.selectedIndex;
return (
<ButtonGroup>
<Button className={idx === 1 ? 'primary' : 'secondary'} onClick={() => this.handleSelection(1)}>MainButtonA</Button>
<Button className={idx === 2 ? 'primary' : 'secondary'} onClick={() => this.handleSelection(2)}>MainButtonB</Button>
<Button className={idx === 3 ? 'primary' : 'secondary'} onClick={() => this.handleSelection(3)}>MainButtonC</Button>
</ButtonGroup>
);
}
}
You cannot get the props of a component from a DOM node. You could instead keep your button names in an array in your component state and use that to render your buttons in the render method.
You can then pass in the button name to the handleSelection and use that as your selected value. If your button is the selected one it can be given the primary color, otherwise the secondary one.
Example
import React from "react";
import ReactDOM from "react-dom";
import { ButtonGroup, Button } from "reactstrap";
import "bootstrap/dist/css/bootstrap.min.css";
class MainButtonsGroup extends React.Component {
constructor(props) {
super(props);
this.state = {
buttons: ["A", "B", "C"],
selected: null
};
}
handleSelection = button => {
this.setState({ selected: button });
};
render() {
const { buttons, selected } = this.state;
return (
<ButtonGroup>
{buttons.map(button => (
<Button
key={button}
onClick={() => this.handleSelection(button)}
color={selected === button ? "primary" : "secondary"}
>
MainButton{button}
</Button>
))}
</ButtonGroup>
);
}
}

Resources