React onClick function doesn't fire - javascript

I built this toy problem to replicate an issue I am facing in a larger app. Why does handleClick not fire when the button is clicked?
const Button = () => <button type="button">Click me</button>;
export const App = () => {
const handleClick = () => {
console.log("clicked");
};
return <Button onClick={handleClick} />;
};

You pass onClick={handleClick} as a property to Button but the Button component does not use the property.
const Button = () ... // ⚠️ no onClick property used
You can fix this by responding to the onClick property -
const Button = ({ onClick }) => // ✅
<button type="button" onClick={onClick}> // ✅
Click me
</button>
An obvious improvement would be to respond to the children property as well. This allows the caller of Button to write anything in place of the static Click me -
const Button = ({ onClick, children }) =>
<button type="button" onClick={onClick}>{children}</button>
<Button onClick={handleRegister}>Register</Button>
<Button onClick={handleLogin}>Login</Button>
<Button onClick={handleSave}>Save</Button>
Note children can be passed as a property. Sometimes you will see components use children in this way. Both examples function identically -
const Button = ({ onClick, children }) =>
<button
type="button"
onClick={onClick}
children={children}
/>
Another common thing for Button components like this is to automatically prevent the default event for the onClick handler. We can also accept a type property and set the default to "button" if the caller does not otherwise specify -
const Button = ({ type = "button", onClick, children, preventDefault = true }) =>
<button
type={type}
onClick={e => {
if (preventDefault) e.preventDefault()
onClick(e)
}}
children={children}
/>
<Button type="submit" onClick={handleRegister}>Register</Button>
<Button onClick={handleReset} preventDefault={false} children="Reset" />

Related

Loop after every keypress in ReactJS

please, I have a problem with easy application and passing parameter from child to parent. I've tried id and its work. But child component doesn't render onClick event in my child component.
Here is the whole parent (file App.js):
import { useState } from 'react';
import Task from './components/task';
function App() {
const [newTask, setNewTask] = useState('');
const [tasks, setTasks] = useState([]);
const handleChange = (e) => {
if (e.target.value.length === 1) {
e.target.value = e.target.value.toUpperCase();
}
setNewTask(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
setTasks([
...tasks,
{
id: [...tasks].length === 0 ? 0 : Math.max(...tasks.map((task) => task.id)) + 1,
name: newTask,
},
]);
setNewTask('');
};
const handleEdit = (buttonValues) => {
console.log(buttonValues.type + ': ' + buttonValues.id);
};
return (
<div className="container">
<h1>Task Manager</h1>
<form onSubmit={handleSubmit}>
<label htmlFor="newTaskInput" className="sr-only">
Input for new task
</label>
<input
id="newTaskInput"
type="text"
onChange={handleChange}
value={newTask}
placeholder="Add new task here..."
autoFocus
/>
</form>
<ul>
{tasks.map((task, index) => {
return <Task id={task.id} name={task.name} key={index} onActionButtonClick={handleEdit} />;
})}
</ul>
</div>
);
}
export default App;
and my child component (file task.jsx) looks like:
import React from 'react';
const Task = (props) => {
const { id, name } = props;
const handleActionButton = (buttonValues) => {
props.onActionButtonClick(buttonValues);
};
return (
<li id={`task-${id}`} data-id={id}>
<span>{name}</span>
<button data-id={id} type="button" className="btn-done" title="Mark task as done" onClick={alert('Done')}>
Done
</button>
<button data-id={id} type="button" className="btn-edit" title="Edit this task" onClick={alert('Edit')}>
Edit
</button>
<button data-id={id} type="button" className="btn-delete" title="Delete this task" onClick={alert('Delete')}>
Delete
</button>
</li>
);
};
export default Task;
If you run it, console log with type and id will show after every type in input field if some task already exists. But if I click on any button, no onClick event is called.
The first problem is, that event from child to parent with button type and id from list should be called only after onClick.
The second problem is that event handleEdit is called after every change in newTask state.
The original code has instead of console.log in onClick event has
...
const handleActionButton = (buttonValues) => {
props.onActionButtonClick(buttonValues);
};
....
return (
...
<button
data-id={id}
type="button"
className="btn-delete"
title="Delete this task"
onClick={handleActionButton({ type: 'delete', id: id })}
...
)
But it doesn't react onClick as well. And if I inspect li element it looks like:
<li id="task-0" data-id="0"><span>Milk</span><button data-id="0" type="button" class="btn-done" title="Mark task as done">Done</button><button data-id="0" type="button" class="btn-edit" title="Edit this task">Edit</button><button data-id="0" type="button" class="btn-delete" title="Delete this task">Delete</button></li>
As you can see, there are no onClick event.
Thank for help
Sure thing. I'll post it as an answer to make it formatted nicely.
In short, there are two terms you must know: reference and invokation.
A function is invoked when you add the ( ) to the end of the function, but an event (like the onClick event), must be passed a function reference (and a function reference is a function without the ( ) after it).
So when adding the anonymous function you are making a reference.
Consider these examples:
// reference
onClick={myFunction}
// invokation (won't work)
onClick={myFunction()}
// with arguments - invokation (won't work)
onClick={myFunction('some text')}
// with arguments - reference
onClick={() => myFunction('some text')}
With the above in mind you must create a reference for the alert to work correctly:
// with arguments - invokation (won't work)
onClick={alert('Done')}
// with arguments - reference
onClick={() => alert('Done')}
It's the same with your handleActionButton, because you need to pass arguments, you must "convert" it to a reference using an anonymous function.
There is also another term that is related to the above, and it's the concept of callbacks. The onClick in reality is just a function that takes another function as an argument, and as we've established, must be a reference. The parameter for the onClick is called a callback.
Consider this example:
function myOnClick(callback) {
// here we invoke the passed in function (callback)
// callback is a callback function that simply
// calls/invokes whatever function you pass in
callback();
}
myOnClick(() => alert('Done'));
Thanks a lot for tip to Bqardi for help.
I need create anonymous function and then I can call alert or handle function. But I cannot why I need create anonymous function for execute. But thanks
Child:
const handleActionButton = (buttonValues) => {
props.onActionButtonClick(buttonValues);
};
...
<button
data-id={id}
type="button"
className="btn-edit"
title="Edit this task"
onClick={() => handleActionButton({ type: 'edit', id: id })} >
Parent:
const handleActionButtonClick = (buttonValues) => {
console.log(buttonValues.type + ': ' + buttonValues.id);
};
...
{tasks.map((task, index) => {
return <Task id={task.id} name={task.name} key={index} onActionButtonClick={handleActionButtonClick} />;
})}

Why a function in react is getting called three times when I am calling only one time

const NewApp = (props) => {
const [ counter, setCounter ] = useState(0)
const incrementCounterByOne = () => setCounter(counter + 1)
const decrementCounterByOne = () => setCounter(counter - 1)
const setCounterToZero = () => setCounter(0)
return (
<div>
<Display counter={counter} />
<Button handleClick={incrementCounterByOne} text="Increment" />
<Button handleClick={decrementCounterByOne} text="Decrement" />
<Button handleClick={setCounterToZero} text="Reset" />
{/* <button onClick={incrementCounterByOne}>Increment</button>
<button onClick={decrementCounterByOne}>Decrement</button>
<button onClick={setCounterToZero}>Reset</button> */}
</div>
)
}
const Button = (props) => {
console.log("clicked")
return (
<button onClick={props.handleClick}>
{props.text}
</button>
)
}
I am a newbie in react. When I am clicking on any button the button function is getting called three times but I am only clicking on one button at a time. I am a little confused on why so. If anyone can explain the process of how this is happening it be very helpful thank you!
Your console.log("clicked") runs when a Button is called:
const Button = (props) => {
console.log("clicked")
and you have three buttons, so you see three logs whenever there's a re-render.
If you wanted to log only when a click occurs (and only log once, not three times), log inside the click handler instead.
<button onClick={(e) => {
console.log('clicked');
return props.handleClick(e);
}}>

onClick() works on the first click only - React

I am trying to make onClick() function work for every button (its the same result), but as of now onClick() works only once.
If I click on a button again (the same or different) the pop up doesn't work anymore.
Could you please help? I cannot figure out how to fix it.
function Challange() {
const [isPopped, setPop] = useState(false);
const pop = () => {
setPop(true);
};
return (
<>
{isPopped && <Dialog2 />}
<div className="challanges">
<h1 className="newchallenge">Choose New Challange</h1>
<button className="challangeBtn" onClick={pop}>
Eat Vegetarian (31days)
</button>
<button className="challangeBtn" onClick={pop}>
Take the bike to work (14days)
</button>
<button className="challangeBtn" onClick={pop}>
Recycle your plastic bottles (31days)
</button>
<button className="challangeBtn" onClick={pop}>
Use public transport to commute (31days)
</button>
<button className="challangeBtn" onClick={pop}>
Don't fly an airplane (365days)
</button>
</div>
</>
);
}
export default Challange;
Your pop function code only sets the state to true, so it will never be false again. You may change it like this.
const pop = () => {
setPop(!isPopped);
};
in your case setPop can take boolean value OR method that returns boolean.
Best practices for handle toggling in React looks like:
const pop = () => {
setPop(currentValue => !currentValue);
};
Also, use useCallback hook to manage events like click and avoid re-initialization of your method on each render:
const pop = useCallback(() => {
setPop(currentValue => !currentValue);
}, []);

React Bootstrap Dropdown Button OnSelect

I am passing the option values into a series of Dropdown buttons, each of which is in a child component from a data array.
When an option is chosen in one of the buttons I am updating the state in the parent component with the result of onSelect. This is all working fine...
//parent component
sourceSelected = (event) => {
this.setState({
sourceSelected: event
});
...
<ButtonToolbar>
{MEDIUM.map((medium) =>
<Medium key={medium.medium_name} medium={medium} onSelectedValue{this.sourceSelected } />
)};
</ButtonToolbar>
//child component
<DropdownButton title={props.medium.medium_name} id="source-dropdown" onSelect={props.onSelectedValue}>
{props.medium.source.map((option, index) =>
<MenuItem key={index} eventKey={option}> {option} </MenuItem>)}
</DropdownButton>
However, I would also like to store in the state (mediumSelected=???) the name of the button from which the option was selected.
Is there anyway to get OnSelect to pass this back or should I do something else?
OK, I answered this using... https://reactjs.org/docs/handling-events.html passing arguments to event handlers.
The code is:
//parent component
sourceSelected = ( medium_name, event) => {
this.setState({
sourceSelected: event,
mediumSelected: medium_name
});
}
...
<div className='test'>
<ButtonToolbar>
{MEDIUM.map((medium) =>
<Medium key={medium.medium_name} medium={medium} onSelectedValue={this.sourceSelected.bind(this, medium.medium_name) } />
)};
</ButtonToolbar>
You can take advantage of Javascript events and this. Basically, pass the event to the function that will be using the button name, like this
<button name="btn" onClick={e => this.buttonName(e.target.name)}>
You will also need to bind this in your constructor()
Example code:
constructor(props) {
super(props);
// Bind this so you can use it below
this.buttonName = this.buttonName.bind(this);
}
buttonName(e) {
console.log(e);
}
render() {
return (
<div>
// Pass the name of the button to the function
<button name="btn" onClick={e => this.buttonName(e.target.name)}>
Button
</button>
</div>
);
}
I also threw a quick example on https://codesandbox.io/s/lrwqr303vz. Don't mind the file names.

Why triggered method?

I create REACT component and passes the method to call by pressing the button, using the Redux connect. There is a code:
const PageClientOne = ({onSubmit, onDelete, client}) => {
return(
<form style={styles.enter} onSubmit={_handleSubmit(client,onSubmit)}>
// ... something code
<Button type="submit" theme="success">Save</Button>
<Button type="button"
theme="error" onClick={onDelete(client._id)}>Delete</Button>
</form>
)
}
const _handleSubmit = (client, onSubmit) => {
return event => {
event.preventDefault()
onSubmit(client)
}
}
const mapStateToProps = (state, ownProps) => {
return {
client: state.reducers.clientsState.client
}
}
const mapDispatchToProps = (dispatch) => ({
onSubmit: (client) => {
dispatch(saveClient(client))
},
onDelete: (id) => {
console.log("DELETE")
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(PageClientOne)
The problem is that onDelete triggered when the page loads, and not only when clicked on the button. What am I doing wrong?
Its slightly different in React.
Change this,
<Button type="button" theme="error" onClick={onDelete(client._id)}>Delete</Button>
to,
<Button type="button" theme="error" onClick={()=>onDelete(client._id)}>Delete</Button>
Read here for more info,
https://facebook.github.io/react/docs/handling-events.html
(I'm not very familiar with React, so this could be wrong...)
When you create your <form> element, you define the button like this:
<Button type="button" theme="error" onClick={onDelete(client._id)}>Delete</Button>
Code in brackets – {onDelete(client._id)} – gets executed when the element is created (at page load), and the result it evaluates to is inserted into the element, not the code itself. You meant to have a normal onclick attribute; so put the code in quotes "" as in usual HTML:
onclick="onDelete(client._id)"
The issue is in onClick
<Button type="button" theme="error" onClick={onDelete(client._id)}>Delete</Button>
Instead of passing the callback function you are actually calling onDelete at the time of element creation.
Try
<Button type="button" theme="error" onClick={onDelete.bind(this, client._id)}>Delete</Button>
Hope it helps.
I hope my answer is corresponding to your question.
Looks like your delete button was submitting the form as you get the page refreshed.
So, you need to prevent the submit.
const mapDispatchToProps = (dispatch) => ({
onSubmit: (client) => {
dispatch(saveClient(client))
},
onDelete: (evt, id) => {
evt.preventDefault()
console.log("DELETE")
}
}
And on your delete button
<Button type="button" theme="error" onClick={(evt) => onDelete(evt, client._id)}>Delete</Button>

Categories

Resources