Props are undefined in React - reactjs

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>
);
}
}

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;

Change Component property through onClick

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

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?

Multiple buttons do the same

I am building an app in React, that is connected to an API I have written before. Buttons are renderizing but all of them change at the same time. I need advice about how can I write my code in order to separate the functionality.
My app renderize with a .map the same number of Buttons as appointments which is an array. All of them change when this.state.shown change but I need to separate all the buttons in order to only show the one that I clicked. Right now, when I clicked in one of them, this.state.shown change its value so all the buttons change because all depends of the same variable. I am looking for advices about how I can separate this.
class AppointmentsList extends Component {
constructor(props) {
super(props);
this.state = {
appointments: [],
isLoading: false,
shown: false, //Variable to know if a button need to change and render the component
customerUp: false
}
this.toggleCustomer = this.toggleCustomer.bind(this);
//this.showCustomer = this.showCustomer.bind(this);
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
How can I reorganize my code in order to render the Buttons separately?
Thanks in advance.
Method 1: You can make shown state a object like:
state = {
shown:{}
}
toggleCustomer(id) {
const updatedShownState = {...this.state.shown};
updatedShownState[id] = updatedShownState[id] ? false : true;
this.setState({
shown: updatedShownState,
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown[app.customer.id] ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer(app.customer.id) }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown[app.customer.id] ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
Method 2: Make a separate component for Button and Customer
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<Appointment key = {app.id} app = {app} />
)}
</div>
)
}
class Appointment extends Component {
state = {
shown: false,
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
}
render() {
const { app } = this.props;
return (
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)
}
}
Let me know if it works and the method you prefer.
You can create a separate component for your button (buttonComponent) inside your AppointmentsList component and pass the shown has props and the in componentDidMount of buttonComponent copy the props to the state of buttonComponent.
This way each button will have its own state, which manages shown.
Button component:
import react from 'react';
interface buttonComponentProps{
shown: boolean;
}
interface buttonComponentState{
shown: boolean;
}
class buttonComponent extends react.Component<buttonComponentProps,{}>{
constructor(props:buttonComponentProps){
super();
this.state{
shown:props.shown
}
}
....
}
export default buttonComponent;

Resources