Loop after every keypress in ReactJS - javascript

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

Related

React onClick function doesn't fire

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" />

First click in child component is undefined in parent component React

I want to implement a "delete contact when clicked" in a simple contact manager app I'm making to learn React but it is very buggy.
The first time I click the item to delete it does nothing and when clicked another time it deletes the previous one. I'm new learning react and don't know why is this happening, someone can help?
const { useState } = React;
function AddPersonForm(props) {
const [person, setPerson] = useState("");
function handleChange(e) {
setPerson(e.target.value);
}
function handleSubmit(e) {
props.handleSubmit(person);
setPerson("");
e.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add new contact"
onChange={handleChange}
value={person}
/>
<button type="submit">Add</button>
</form>
);
}
function PeopleList(props) {
const [person_, setPerson_] = useState("");
function handleCLick(e) {
setPerson_(e.target.textContent);
props.handleCLick(person_);
}
return (
<ul onClick={handleCLick}>
{props.data.map((val, index) => (
<li key={index}>{val}</li>
))}
</ul>
);
}
function ContactManager(props) {
const [contacts, setContacts] = useState(props.data);
function addPerson(name) {
setContacts([...contacts, name]);
}
function removePerson(name_) {
let filtered = contacts.filter(function (value) {
return value != name_;
});
setContacts(filtered);
}
return (
<div>
<AddPersonForm handleSubmit={addPerson} />
<PeopleList data={contacts} handleCLick={removePerson} />
</div>
);
}
const contacts = ["James Smith", "Thomas Anderson", "Bruce Wayne"];
ReactDOM.render(
<ContactManager data={contacts} />,
document.getElementById("root")
);
Issue
Having the onClick handler on the unordered list element seems a bit odd to me, but your issue a misunderstanding of when React state updates. React state updates are enqueued and asynchronously processed. In other words, when you enqueue the state update setPerson_(e.target.textContent) in handleClick in PeopleList, that person_ hasn't updated on the next line props.handleCLick(person_);. You are using state that hasn't updated yet.
Solution
Just send the click event object's value to the props.handleClick callback directly. You can also remove the local person_ state.
function PeopleList(props) {
function handleCLick(e) {
props.handleCLick(e.target.textContent);
}
return (
<ul onClick={handleCLick}>
{props.data.map((val, index) => (
<li key={index}>{val}</li>
))}
</ul>
);
}

Pass value using button from array

I'm learning to use React and I have a problem about how to pass the id using a button.
I have recover data for my fetch from: https://jsonplaceholder.typicode.com/users
My js page:
handleClick(){
alert("ALERT " + this.props);
}
render(){
const listMeetings = this.props.meetings.map((meeting) =>
<li key={meeting.id}>{meeting.name}
<button key={meeting.id} onClick={() => this.handleClick(meeting.id)} value={meeting.id}>{'Details'}</button>
</li>);
return(
<div>
<ul>{listMeetings}</ul>
</div>
)
}
}
So at the moment I have a list of name and a button, immediately next to each name.
What I would to do is to pass the id of every name, when the button is clicked (at the moment I would print only an alert with the id.
With my code, I have understand that I pass all the array (so I have 10 object).
How can I do? thank you.
You have an error in your handleClick you pass the meeting.id as a param but it's missing in the functions declaration
handleClick(id){
alert("ALERT " + id);
}
render(){
const listMeetings = this.props.meetings.map((meeting) =>
<li key={meeting.id}>{meeting.name}
<button key={meeting.id} onClick={() => this.handleClick(meeting.id)} value={meeting.id}>{'Details'}</button>
</li>);
return(
<div>
<ul>{listMeetings}</ul>
</div>
)
}
}
Try anonymous function version instead:
handleClick = (id){
alert("ALERT " + id);
}
render(){
const listMeetings = this.props.meetings.map((meeting) =>
<li key={meeting.id}>{meeting.name}
<button key={meeting.id} onClick={(id) => this.handleClick(meeting.id)} value={meeting.id}>{'Details'}</button>
</li>);
return(
<div>
<ul>{listMeetings}</ul>
</div>
)
}

React Redux: Posting Records partly work with onclick function

I have the following code in React Redux. Everything works fine by posting records to server backend as per code below.
handlePostId(postid,post_content){
return (e) => this.props.dispatch(Actions.sendData(postid,post_content));
}
<input type="button" value="Post" onClick={ this.handlePostId(post1.id, 55)} />
Here is my issue.
If I change onclick function to code below
<input type="button" value="Post" onClick={() => this.handlePostId(post1.id, 55)} />
It will not post records to server backend. And no error is shown in the console is it because of this () =>. please how do I rectify this
Here is the entire code
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { Actions } from '../actions';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.dispatch(Actions.getRec());
}
handlePostId(postid,post_content){
return (e) => this.props.dispatch(Actions.SendData(postid,post_content));
}
render() {
const { post1, posts1} = this.props;
return (
<div>
{posts1.items1 &&
<ul>
{posts1.items1.map((post1, index1) =>
<li key={post1.id}>
{post1.content} ({post1.id})
<input type="button" value="Post working" onClick={ this.handlePostId(post1.id, 55)} />
<input type="button" value="Post Not Working" onClick={() => this.handlePostId(post1.id, 55)} />
</li>
)}
</ul>
}
</div>
);
}
}
function mapStateToProps(state) {
const { posts1, post1 } = state;
return {
post1,
posts1
};
}
const connectedApp = connect(mapStateToProps)(App );
export { connectedApp as App };
As the other people said, you're returning a function that returns a function that does something, instead of returning a function that does something. You should choose between:
handlePostId(postid, post_content) {
return (e) => this.props.dispatch(Actions.SendData(postid,post_content));
}
// ..
onClick={this.handlePostId(post1.id, 55)}
and
handlePostId(postid, post_content) {
return this.props.dispatch(Actions.SendData(postid,post_content));
}
// ..
onClick={() => this.handlePostId(post1.id, 55)}
Alternatively, you can add a mapDispatchToProps and declare it there, like
function mapDispatchToProps(dispatch) {
return {
handlePostId: (postid, post_content) => {
return dispatch(Actions.SendData(postid, post_content));
},
};
}
const connectedApp = connect(mapStateToProps, mapDispatchToProps)(App);
and in your render method you can do
// ...
const { post1, posts1, handlePostId } = this.props;
// ...
onClick={() => handlePostId(post1.id, 55)}
handlePostId(postid,post_content){
return (e) => this.props.dispatch(Actions.sendData(postid,post_content));
}
This returns a function which takes a parameter (presumably an event object):
<input type="button" value="Post" onClick={this.handlePostId(post1.id, 55)} />
In order to rewrite the <input/> tag as
<input type="button" value="Post" onClick={() => this.handlePostId(post1.id, 55)} />
you need to also modify handlePostId() to handle the event directly rather than returning a function:
handlePostId(postid,post_content){
return this.props.dispatch(Actions.sendData(postid,post_content));
}
You are using a curried function in your handlePostId method. This means you are returning another function from it. By doing that you can use event object without using an arrow function in your click handler. So this works:
onClick={this.handlePostId(post1.id, 55)}
But if you add an onclick handler again (arrow function) you should invoke it like that:
onClick={() => this.handlePostId(post1.id, 55)()}
Notice the extra () at the end of the function. But, you are not using event object in your handler method, hence you can simply remove the extra function and return your action dispatching like that:
handlePostId(postid,post_content) {
return this.props.dispatch(Actions.sendData(postid,post_content));
and use it like:
onClick={() => this.handlePostId(post1.id, 55)}
Also, if you want to use event object like that and prefer using a curried function then you shouldn't use arrow function in your onclick handler.
onClick={this.handlePostId(post1.id, 55)}
This is enough to use the event object and curried function's goodies. But since you are not using event object I'm not quite sure you wrote this curried function deliberately.

ReactJs multiple functions call inline

My target is to call multiple functions onClick. Assume that some of them has taken event and some value, so it is unable to call them in single function. How can I achieve that?
render() {
//describe first function (1)
const amountSelectClearInput = function(event) {
// call two functions here
this.amountSelect(event);
this.clearInput();
}.bind(this);
var self = this;
return (
<div>
<div>
{amounts.map(function (name, index) {
return <input type="button" value={name}
className={self.state.active === name ? 'active' : ''}
//here I must call () => self.changeClass(name) (1), and amountSelectClearInput (2)
onClick={() => self.changeClass(name)} key={ name }/>;
})}
</div>
</div>
);
}
I need to call amountSelectClearInput and () => self.changeClass(name) at same time
onClick={function() { amountSelectClearInput(event); self.changeClass(name)}}

Categories

Resources