I'm doing a very simple two button state. where if i click abutton, A component displays and if bbutton is clicked then component B. I'm mapping through array of items so that each of them have their own buttons state. Lets say if I click item 1's button B then I want only first Item B to show. Right now All of them gets triggered at once. I have bounded each of them in the constructor but still i'm unable to get only the once the once clicked to trigger and show the relevant component.
class Home extends Component {
constructor(props) {
super(props);
this.state = {
lists: []
showA: true,
showB: false
}
this.aButtonHandler = this.aButtonHandler.bind(this);
this.bButtonHandler = this.bButtonHandler.bind(this);
}
aButtonHandler = (e) => {
this.setState({
showA: true,
showB: false
})
}
bButtonHandler = (e) => {
this.setState({
showA: false,
showB: true
})
}
render(){
return (
<div>
{this.state.lists.map(detail =>
<li>{detail.id}</li>
<button onClick={(e) => this.aButtonHandler(e)} >see A</button>
<button onClick={(e) => this.bButtonHandler(e)} >see B</button>
{this.state.showA ?
<ComponentA /> : null
}
{this.state.showB ?
<ComponentB /> : null
}
)}
</div>
)
}
If you are using arrow functions no need to bind functions.
If you want to bind then change it to normal function like this.
aButtonHandler(e){...}
bButtonHandler(e){...}
If you want to use bind in constructor no need to use arrow function, just use regular functions and pass the function directly to onClick
aButtonHandler(e) { this.setState({ showA: true, showB: false }); }
bButtonHandler(e) { this.setState({ showA: false, showB: true }); }
render() {
return (
<div>
{this.state.lists.map(detail => (
<div>
<li>{detail.id}</li>
<button onClick={this.aButtonHandler}>see A</button>
<button onClick={this.bButtonHandler}>see B</button>
{this.state.showA ? <ComponentA /> : null}
{this.state.showA ? <ComponentB /> : null}
</div>
))}
</div>
);
Related
Onclick function execute the myChangeHandler, which changes the state to opposite on every click. This will toggle the content inside h1 element. Here the function execute the change for both button. Any possibility to change that behaviour for individual button?
class File extends React.Component {
constructor(props) {
super(props);
this.state = {
user: false,
admin:false
};
this.myChangeHandler = this.myChangeHandler.bind(this);
}
myChangeHandler() {
this.setState(state => ({
user:!state.user
admin:!state.admin
}));
}
render() {
return(
<div> <button onClick={this.myChangeHandler}>Toggle admin </button>
{this.state.display && <h1>admin online!</h1>} </div>
<div> <button onClick={this.myChangeHandler}>Toggle user </button>
{this.state.display && <h1>user online!</h1>} </div>
)
}
}
You can give the buttons a name and access those in the handler:
<button name='admin' onClick={this.myChangeHandler}>Toggle admin </button>
myChangeHandler(e) {
const id = e.target.name
this.setState((state) => ({
[id]: !state[id]
}));
}
Note that you have to save the id before the setState, because setState is async and the event will be removed after the function. So if you try to access the event during the delayed setState, the name would be null.
Sandbox
You can pass a reference to the function to tell which button you clicked on:
myChangeHandler(name) {
this.setState((prev) => ({ [name]: !prev[name] }));
}
render() {
return(
<div>
<button onClick={() => this.myChangeHandler('admin')}>Toggle admin </button>
{this.state.display && <h1>admin online!</h1>}
</div>
<div>
<button onClick={() => this.myChangeHandler('user')}>Toggle user</button>
{this.state.display && <h1>user online!</h1>}
</div>
)
}
I'm trying to return a component when I click on a button.
(a delete button with a confirmation popup) I've tried some examples I found online but none really worked for me.
Here is my code:
class DeleteTask extends React.Component {
handleClick() {
return <TaskDeleteModal />
}
render() {
return (
<div>
<button type="button" className={styles.deletetask} onClick={() => this.handleClick()}>Verwijderen</button>;
</div>
)
}
}
Thanks for reading, I hope you can assist me.
It did not work because the return <TaskDeleteModal /> of handleClick does not add <TaskDeleteModal /> to your render function.
You need to add <TaskDeleteModal /> to render function and control it visibility by a state:
Try the following code:
class DeleteTask extends React.Component {
this.state = {
showModal: false
}
handleClick() {
this.setState({showModal: true})
}
render() {
return (
<div>
<button type="button" className={styles.deletetask} onClick={() => this.handleClick()}>Verwijderen</button>;
{/* In TaskDeleteModal you might have a button, when you click on that button call this.setState({showModal: false}) to hide the modal */}
{this.state.showModal && <TaskDeleteModal />}
</div>
)
}
}
class DeleteTask extends React.Component {
this.state = {
showModal: false
}
handleClick() {
this.setState({showModal: true})
}
render() {
return (
<div>
<button type="button" className={styles.deletetask} onClick={() => this.handleClick()}>Verwijderen</button>;
{/* In TaskDeleteModal you might have a button, when you click on that button call this.setState({showModal: false}) to hide the modal */}
{this.state.showModal && <TaskDeleteModal />}
</div>
)
}
}
I'm using Gatsby for a static website.
My page is composed of two parts. Section 1 and Section 2.
I want to hide an image in Section 1, when a button is hovered in Section 2.
If I clean up a bit my .js, it looks like that :
<section>
<SomeText/>
<DefaultImage />
<ImageOne />
<ImageTwo />
</section>
<section>
<Button1/>
<Button2/>
</section>
What I want to achieve:
By default, <DefaultImage/> is shown.
If I hover <Button1>, I want to hide <DefaultImage/> and display <ImageOne/> instead.
Same goes for <Button2/>, which, when hovered, should hide <DefaultImage/> and display <ImageTwo/>.
I've read about onMouseEnter and onMouseLeave, and I think that the answer lies there but couldn't make it work for now.
Thank you for your ideas!
Maybe I can also pass a prop (like a css class) on the "to be hidden" component when the other is hovered
I managed to do it (check the accepted answer).
Here is my edited code:
class Parent extends Component {
state = {
isHoveringImage1: false
}
state = {
isNotHovering: false
}
state = {
isHoveringImage2: false
}
startHoverMasque = () => this.setState({ isHoveringMasque: true, isNotHovering: true})
stopHoverMasque = () => this.setState({ isHoveringMasque: false, isNotHovering: false })
startHoverMains = () => this.setState({ isHoveringMains: true, isNotHovering: true})
stopHoverMains = () => this.setState({ isHoveringMains: false, isNotHovering: false })
render() {
return (
<>
<Global
styles={globalStyles}/>
<section>
{
this.state.isNotHovering
? <ImageDefaultHidden />
: <ImageDefault/>
}
{
this.state.isHoveringImage1
? <Image1 />
: <ImageDefaultHidden />
}
{
this.state.isHoveringImage2
? <Image2 />
: <ImageDefaultHidden />
}
</section>
<section>
<Button1
onMouseEnter={ this.startHoverImage1}
onMouseLeave={ this.stopHoverImage1 }
>Bouton1</Button1>
<Button2
onMouseEnter={ this.startHoverImage2}
onMouseLeave={ this.stopHoverImage2 }
>Bouton 2</Button2>
</section>
</>
)
}
}
export default Parent```
You can annotate when the mouse enter and leaves the target Button in the state of your parent component:
class Parent extends Component {
state = {
isHovering: false
}
startHover = () => this.setState({ isHovering: true })
stopHover = () => this.setState({ isHovering: false })
render() {
return (
<>
<section>
<SomeText/>
{
this.state.isHovering
? <ImageOne />
: <DefaultImage />
}
<ImageTwo />
</section>
<section>
<Button1
onMouseEnter={ this.startHover }
onMouseLeave={ this.stopHover }
/>
<Button2/>
</section>
</>
)
}
}
The solution is to include the variable saying whether or not your image should be rendered in your parent component's state.
To set this variable, pass down a function to the component containing the button and bind it to the events you gave in your question : onMouseEnter and onMouseLeave.
Working example :
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
hideImage: false
}
}
toggleImage = hideImage => ev => {
this.setState({ hideImage })
}
render = () => {
return(
<div>
<ButtonComponent hovered={this.toggleImage}/>
<ImageComponent isHidden={this.state.hideImage}/>
</div>
)
}
}
const ButtonComponent = ({ hovered }) => <button onMouseEnter={hovered(true)} onMouseLeave={hovered(false)}>Hover me :)</button>
const ImageComponent = ({ isHidden }) => <img hidden={isHidden} src='https://reactjs.org/logo-og.png'/>
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.1/umd/react-dom.production.min.js"></script>
<div id='root'>
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;
Is there a way to avoid passing the same handler as a prop to children component every time ?
I create a Popup component and i want it to update the sate before closing
closePoPup = (e) => {
console.log('closePoPup');
e.preventDefault()
this.setState({
renderProgramePoPup: false,
renderTreeListPoPup: false,
})
}
.....
<Popup title="..." classes="..." closePoPup={this.closePoPup}>
<Foo ... />
</Popup
But i have to put the prop closePoPup={this.closePoPup} in every component like bellow :
class ProjectForm extends React.Component {
componentWillMount() {
this.setState({
renderProgramePoPup: false,
renderTreeListPoPup: false,
});
}
....
closePoPup = (e) => {
e.preventDefault()
this.setState({
renderProgramePoPup: false,
renderTreeListPoPup: false,
})
}
....
render() {
return (
<div>
...
{(this.state.renderProgramePoPup ?
<Popup title="..." classes="..."
closePoPup={this.closePoPup}>
<SimplePopUp ... />
</Popup> : null
)}
{(this.state.renderTreeListPoPup ?
<Popup title="..." classes="..."
closePoPup={this.closePoPup}>
<TreeList ... />
</Popup> : null
)}
</div>
)
}
}
You can construct a wrapper:
render() {
const PopupWithCloseProp = props => <Popup closePoPup={this.closePoPup} {...props} />;
return (
<div>
<PopupWithCloseProp someProp />
<PopupWithCloseProp someDiffProp />
</div>
);
}
Or a generic factory if you have different types of components needing closePoPup:
const createComponentWithCloseProp = Component => props => (
<Component closePoPup={this.closePoPup} {...props} />
);
const PopupWithCloseProp = createComponentWithCloseProp(Popup);
One potential solution is to create a list or object this.state.renderPopUp. Then you can map() over the list (or Object.keys()) to create a list of Popups. You can extend this idea even further by including data for each Popup in the list.