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>
)
}
Related
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} />;
})}
I am having hard times with this one.
I created a modal (SetLevel) so the user can select a level and after that what I want is to just update my initial state which goes by the name of level . So I pass my prop in handleChange in the SetLevel component like this:
const Game = () => {
const [levelOpen,setlevelOpen]=useState(false);
const [level,setlevel]=useState(1);
const changedLevel = (newLevel)=>{
console.log('newLevel',newLevel);
setlevel(newLevel);
}
return (
<div>
<h1 className='title'>Find the icons </h1>
<div className='container'>
<button className='btn' onClick={() => setlevelOpen(true)}>
Select level
</button>
<SetLevel isOpen={levelOpen} handleChange={()=>changedLevel(level)}
onClose={()=>setlevelOpen(false)}/>
</div>
<ItemDrag newLevel={level}/>
</div>
);
};
SetLevel child component looks like this:
const SetLevel = ({isOpen,onClose,handleChange}) => {
if (isOpen === false) return null;
const close = (e) => {
e.preventDefault();
if (onClose) {
onClose();
}
};
const handleClick =(num,e)=>{
console.log(num);
handleChange(num)
close(e)
}
return (
<div className='modal-window'>
<div>
<div onClick={(e) => close(e)} className='modal-close'>
CLOSE
</div>
<h1>Select level!</h1>
<button className="btn" onClick={(e)=>handleClick(3,e)} >LEVEL3</button>
</div>
</div>
);
}
So here is how I do it if a user selects level 3 I pass that number to my handleClick function and this function should take care of that handleChange prop as you can see.
But whenever I do this my level is not updating how come?? The value i am getting back is always 1 , why is that? thanks.
You don't take the return value of the handleChange in the Game component.
Try it like this:
const Game = () => {
const [levelOpen,setlevelOpen]=useState(false);
const [level,setlevel]=useState(1);
const changedLevel = (newLevel)=>{
console.log('newLevel',newLevel);
setlevel(newLevel);
}
return (
<div>
<h1 className='title'>Find the icons </h1>
<div className='container'>
<button className='btn' onClick={() => setlevelOpen(true)}>
Select level
</button>
<SetLevel isOpen={levelOpen} handleChange={(newLevel)=>changedLevel(newLevel)}
onClose={()=>setlevelOpen(false)}/>
</div>
<ItemDrag newLevel={level}/>
</div>
);
};
I have this function
renderCompanies() {
if (this.props.companies)
return [
<div>
Dashboard hello <div>{this.renderProfile()}</div>
<div>
{this.props.companies.map(function(item, i) {
return (
<div>
<div
key={i}
onClick={item => {
this.setState({ currentCompany: item });
}}
>
{i}: {item.name}
</div>
<button>Delete Company</button>
</div>
);
})}
</div>
<AddCompanyPopUp />
</div>
];
}
I want to loop though this.props.companies and render a list of items. I want a user to be able to click on a specific item and have the item be saved to state.
This function runs inside another funtion
renderEitherMenuOrCompanyList() {
if (this.state.currentCompany) {
return <Menu companies={this.state.currentCompany} />;
} else {
return <div>{this.renderCompanies()}</div>;
}
}
Both are already bound to this
this.renderCompanies = this.renderCompanies.bind(this);
this.renderProfile = this.renderProfile.bind(this);
this.renderEitherMenuOrCompanyList = this.renderEitherMenuOrCompanyList.bind(this)
The renderEitherMenuOrCompanyList function is being called inside the render react function/method.
My problem is that I cannot set the state from the renderCompanies .map function. I keep getting "Cannot read property 'setState' of undefined" . This should be simple but I have not been able to do it
Make sure the function given to map is bound as well, or an arrow function:
{this.props.companies.map((item, i) => {
return (
<div>
<div
key={i}
onClick={() => {
this.setState({ currentCompany: item });
}}
>
{i}: {item.name}
</div>
<button>Delete Company</button>
</div>
);
})}
The function passed to this.props.companies.map isn’t an arrow function, so it creates a new this. Change it to an arrow function to preserve the this from outside of it.
this.props.companies.map( ( item, i ) => { ... } )
You’ve also named the argument to onClick item, but it’s actually the click event. You want the item already defined by the map function. Name the argument to onClick something else, or nothing, to avoid overwriting the item variable you actually want.
onClick={ () => { ... } }
I would like to get the text content of the div, on click.
I have tried to add ref's to my syntax but it doesn't work when I try to do const ref = this.refName.textContent.
So what I would like to achieve is on click of recipe-container, store the text content of the h3 and ul into variables, where the variable are in a parent component.
This is my child component here:
export default class Recipecontainer extends React.Component {
render(){
const recipe = this.props.recipe;
let recipeIngredients = recipe.ingredients;
recipeIngredients = recipeIngredients.map((item, index) => <li key={index}>{item}</li>);
return (
<div className="recipe-container">
<h3 className="recipe-name">{recipe.recipeName}</h3>
<div className="food-img">
<img src={recipe.smallImageUrls} alt=""/>
</div>
<ul className="ingredient-list">{recipeIngredients}</ul>
</div>
)
}
And this is the method that I would plan to use, which is in my parent component, to save these text content into variables; but of course what i have written below does not work.
saveData(e){
const recipeName = document.getElementsByClassName(".recipe-name").textContent;
}
Any help is appreciated!
instead of textCOntent use innerHTML
const recipeName = document.getElementsByClassName(".recipe-name").innerHTML;
I think you don't need the html itself, You need to pass smallImageUrls & smallImageUrls to parent component when user click the div so Use the react way:
<div className="recipe-container" onClick={() => this.props.onDivClick(recipe.recipeName, recipe.smallImageUrls)}>
You can also do something like this;
<div className="recipe-container" onClick={(e) => console.log(e.target.textContent)}>
You can add an onClick callback to your props, and use that to callback , with the recipe data, when the <div> is clicked in the child component. Like so:
export default class Recipecontainer extends React.Component {
handleClick = () => {
const { onClick, recipe: { recipeName, recipeIngredients } } = this.props;
recipeIngredients = recipeIngredients.map((item, index) => ( index, item ));
onClick({ recipeName, recipeIngredients });
}
render(){
const recipe = this.props.recipe;
let recipeIngredients = recipe.ingredients;
recipeIngredients = recipeIngredients.map((item, index) => <li key={index}>{item}</li>);
return (
<div className="recipe-container" onClick={this.handleClick} >
<h3 className="recipe-name">{recipe.recipeName}</h3>
<div className="food-img">
<img src={recipe.smallImageUrls} alt=""/>
</div>
<ul className="ingredient-list">{recipeIngredients}</ul>
</div>
)
}
If you absolutely need access to the DOM element of the child (I really don't think you do), the documentation recommends a callback prop attached to the DOM as a ref attribute, like so:
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}
$('#here').bind('click', function(event) {
this.toggleModal.bind(this, items.base_image, items.product_name, items.base_price);
});
function toggleModal(image, name, price) {
console.log(image);
console.log(name);
console.log(price);
const popupimage = image;
console.log(popupimage);
this.setState({
showModal: !this.state.showModal
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div className={styles.product_cart} key={index} id="here"></div>
It will work for me try to pass all the values what you need in function ....
I want to listen for click events on an arbitrary number of elements and inside the click event handler, I want to retrieve some info about the clicked element (info which was easily accessible during element creation).
A common solution to this problem is this:
render() {
return (
<div>
{this.props.users.map(user => (
<button onClick={() => this.buttonClicked(user.email)}>
{user.name}
</button>
))}
</div>
);
}
The flaw I see in this approach is that we're creating a new function for every element. Is that a problem worth solving? If it is, how do you feel about this solution:
render() {
return (
<div>
{this.props.users.map((user, index) => (
<button data-index={index} onClick={this.buttonClicked}>
{user.name}
</button>
))}
</div>
);
}
buttonClicked(event) {
const { index } = event.currentTarget.dataset;
const { email } = this.props.users[index];
// ...
}
Create another component and dispatch the event from it.
class Button extends React.PureComponent{
handleOnClick = ()=>{
const {onClick, ...rest} = this.props
if(onClick typeof ==='function'){
onClick(rest)
}
}
render(){
const {name} = this.props
return (
<button onClick={this.handleOnClick}>
{name}
</button>)
}
}
...
render() {
return (
<div>
{this.props.users.map(user => (
<Button {...user} key={user.email} onClick={this.buttonClicked} />
))}
</div>
);
}
I believe it is better to use a defined function instead of anonymous/inline functions, otherwise the onClick handler only gets created during the render stage.
In your second example, you can actually bind the argument to the handler:
render() {
return (
<div>
{this.props.users.map((user, index) => (
<button data-index={index} onClick={this.buttonClicked.bind(this, user.email)}>
{user.name}
</button>
))}
</div>
);
}
Secondly, wanted to point out that there is no performance issue with having many handlers. React uses one event handler at the root of your document.