I am trying to send the id of the respective object through an on-click event , yet I always end up being sent the synthetic onClick event , how can I change this ?
How my array of objects looks like :
[
{
id:uuidv4(),
data:[]
}
]
My onClick={(id)=>handleOpen(id)}
EDIT: It was recommended to change the renderig of the button in the following way :
<button onClick={(e)=>addPieChartGroup(e.target.id)}>Add</button>
Thank you very much !
you can also do like that so when ever your items got update the map function adjust all index automatically
const dataArray = [ { id:uuidv4(), data:[] } ]
handleClick = (dataObj) =>{ console.log(dataObj) }
In render
dataArray.map((obj, index) => <Button id={index} onClick={handleClick(obj)}> Click Me </Button> )
The first param inside the event handler is always the event itself. You are not changing anything by using id, except the argument name. That way you can access event by accessing id variable, but your id variable still refers to event.
You need to access the correct property to get the id.
onClick={(event)=>handleOpen(event.target.id)}
you can pass id of button tag like this
const handleClick = (id) =>{
console.log(id)
}
<Button id='myButton' onClick={(event) => handleClick(event.target.id)}
Related
I am fetching a list of posts from an API and displaying them on webpage. Now, there is a Delete button associated with each post which when clicked should remove the post.
index.html
<template id="single-post">
<li class="post-item">
<h2></h2>
<p></p>
<button>DELETE</button>
</li>
</template>
<ul class="posts"></ul>
app.js
const listElement = document.querySelector('.posts');
const postTemplate = document.getElementById('single-post');
const listOfPosts = await sendHttpRequest(
'GET',
'https://jsonplaceholder.typicode.com/posts'
);
// listOfPosts is already in parsed format
for (const post of listOfPosts) {
const postEl = document.importNode(postTemplate.content, true);
postEl.querySelector('h2').textContent = post.title.toUpperCase();
postEl.querySelector('p').textContent = post.body;
listElement.append(postEl);
const btn = postEl.querySelector('button');
console.log(btn, postEl);
btn.addEventListener('click', () => {
postEl.remove();
});
}
The above code only fetches first post only and throws
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'addEventListener')
at HTMLButtonElement.fetchPosts
When I remove the Event Listener, the code works fine.
I guess this is something to do with importNode method since I have done similar things with createElement and they worked fine
EDIT
I did some little experimenting. The JSON post object returned by API also consisted of an id field. So, I basically added that id to each button that was being created.
Another thing is I used event delegation to remove() the li whose button is clicked.
And very surprisingly It works
const listElement = document.querySelector('.posts');
const postTemplate = document.getElementById('single-post');
const listOfPosts = await sendHttpRequest(
'GET',
'https://jsonplaceholder.typicode.com/posts'
);
// listOfPosts is already in parsed format
for (const post of listOfPosts) {
const postEl = document.importNode(postTemplate.content, true);
postEl.querySelector('h2').textContent = post.title.toUpperCase();
postEl.querySelector('p').textContent = post.body;
postEl.querySelector('button').id = post.id; // HERE
listElement.append(postEl);
}
// delete the li element
listElement.addEventListener('click', (event) => {
if(event.target.tagName === 'BUTTON') {
console.log(event.target);
event.target.parentElement.remove();
}
})
when clicked on first list post's DELETE button, it consoles
<button id="1">DELETE</button>
and removes that item.
This bascially proves that the button tag is certainly there since we are able to query select and set its id.
Strangely, when consoled it shows null.
Your code errors out here:
btn.addEventListener('click', () => {
postEl.remove();
});
The error message clarifies that btn is null, which means that postEl.querySelector('button') returned null, which means that there is no button tag inside postEl.
You will need to carefully look at the result of document.importNode(postTemplate.content, true) and see what it contains. You will see that it does not contain a button tag. So, either the button was not added, in which case you will need to adjust importNode, or, the button is not a button tag, but something else, like <input type="button" value="foo"> for example.
Suppose I have a react app, and I have component A and component CountryCard, suppose that component A displays several times component CountryCard, as a deck of cards, by using a map instruction as in
{this.props.countries.map(country => <CountryCard country={country}...
How do I handle a click on one CountryCard?, is it enough that I use onClick, as
{this.props.countries.map(country => <CountryCard country={country} onClick={this.handleCountryClick(country)}...
or do I have to add an event listener, how do I do that?
Thanks in advance
Rafael
You should just pass this.handleCountryClick to CardComponent
{this.props.countries.map(country => <CountryCard country={country} onCountryClick={this.handleCountryClick}...
then call props.onCountryClick inside of CountryCard, example:
<div onClick={() => this.props.onCountryClick(this.props.country)}></div>
Events should be used as there is a call back on events:
onClick={e => this.handleCountryClick(country)}
or
onClick={this.handleCountryClick}
onClick goes on Dom elements
const CountryCard = ({onClick}) => (<div onclick = {onClick}></div>)
/*maybe do a data attribute if you don't want `country` in perantasis*/
const handleCountryClick = (e) => {
var attr = e.currentTarget.getAttribute("country")
}
Here's an example of a functional component
const CountryCard = ({onClick, country}) => (<div country = {country} onclick = {onClick}></div>)
I'm just giving you an option to use data attribute so you don't have to call an event inline.
And reminding you that click events go on Dom Elements.
I don't use React classes in my work.
I have created a filter() method that returns an new array if certain condition is met.
Function to filter array:
filterTableData = () => {
const userPackages: any = this.props.lenders.packages;
const userPackageFiltered = userPackages.filter((userPackage) => {
return userPackage.businessStatus.toLowerCase() === this.state.btnValue;
});
console.log(userPackageFiltered);
}
Then, I'm using onClick React event handler to trigger the function
like so:
<button
value={this.state.btnValue}
onClick={this.filterTableData}
>
Invited
</button>
It works perfectly!
Now, I want to reuse this logic & attach it to different buttons. So, I am trying to pass parameters to my filterTableData() function like so:
filterTableData = (parameters) => {
const userPackages = this.props.lenders.packages;
const userPackageFiltered = userPackages.filter((parameters) => {
return parameters.toLowerCase() === this.state.btnValue;
});
console.log(userPackageFiltered);
}
Then, I tried to call it like so:
<button
value={this.state.btnValue}
onClick={this.filterTableData(userPackage.businessStatus)}
>
Invited
</button>
<button
value={this.state.btnValue2}
onClick={this.filterTableData(userPackage.type)}
>
Draft
</button>
Obviously, this isn't working.
Here it is a sample code. I want to pass the filter value into a parameter to reuse this code in other buttons & filter the array with different criteria.
The behavior is similar to this code sample, make sure to check the "View Column" icon & the behavior of the checkboxes.
If the aim is to filter based on the truthiness of a particular field in your data, whose name is held in the component as this.state.buttonValue, then you can achieve the desired effect by writing your filter method like this:
filterTableData(param) {
const filteredData = this.state.data.filter(user => user[param]);
// code to use filterdData, presumably using this.setState somewhere
}
And then define your onClick attribute as follows:
onClick={() => this.filterTableData(this.state.buttonValue)}
It's likely though that you don't just want to use the truthiness/falsiness of the values (they will likely only be falsy if they're not actually provided) - you might want an "age" filter to only select users aged over 18, for example. It's not clear what your exact needs are - but the above code should serve as a good outline, the only change will be in filterTableData where you may have to use a switch on the param argument and define a custom filter function for each, which you then pass in to this.state.data.filter. Hopefully you can work out from this what exactly you need to do for your situation.
If I am understanding this correctly, you want to supply a custom parameter along with the onClick event. To do this, you will need to use an anonymous function.
You can either use the anonymous function inline, within the onClick prop (FirstTest), or you can use it on the handler (SecondTest).
Please see the following example, and let me know if this is what you're looking for.
const mybuttons = [{
name: "Invited",
value: "__some__INVITED__value__"
}, {
name: "Draft",
value: "__some__DRAFT__value__"
}]
function FormTest() {
const handleClick = parameter => event => {
switch(parameter.name){
case "Invited": {
alert("You clicked: INVITED! Value of: " + parameter.value);
break;
}
case "Draft": {
alert("You clicked: DRAFT! Value of: " + parameter.value);
break;
}
default:
break;
}
}
return(
<div>
{mybuttons.map(button => {
return <button onClick={handleClick(button)}>{button.name}</button>
})}
</div>
);
}
/**
* FIRST TEST
*/
function FirstTest() {
const handleClick = parameter => {
alert(parameter);
}
return(
<div>
<button onClick={() => handleClick("[FirstTest] My Custom Parameter 1!")}>
First Test
</button>
</div>
);
}
/**
* SECOND TEST
*/
function SecondTest() {
const handleClick = parameter => clickevent => {
alert(parameter);
}
return(
<div>
<button onClick={handleClick("[SecondTest] My Custom Parameter 2!")}>
Second Test
</button>
</div>
);
}
function App(){
return(
<div>
<FormTest />
<hr />
<br />
<p>Original Answer:</p>
<FirstTest />
<SecondTest />
</div>
);
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
I have started an application which I want to work same as weather.com next 36 hours section. The idea is when you click on each weatherCard which has a seperate component in my app you will update the below section which is my weatherDetails component based on the selected weatherCard /weather box. So I made the entire component clickable by giving it the click event via props from my stateful component which is my weatherLocation component. This is my WeatherCard component:
const WeatherCard = (props) => (
<div id={props.date} className="weatherCard" onClick={props.clicked}>
<h2 className="cardDate">{props.date}</h2>
<h4>{props.forcast}</h4>
<div className="minmaxDeg">
<data>{props.min}</data>
<data>{props.max}</data>
</div>
<data>{props.rain}</data>
</div>
);
And here in render method in WeatherLocation component I loop through data coming from state and give props the WeatherCard component:
const WeatherCards = this.state.reports.map( report => {
return(
<WeatherCard
key={report.id}
{...report}
clicked={() => this.handleCardClick(event)}
/>
);
});
And this is the handleCardClick that I added for it just for testing:
handleCardClick = event => {
// const { reports , selectedCardInfo , activeCard } = this.state;
const selectedDate = document.getElementById(event.target.id);
console.log(event.target.id);
}
I don't want to use anchor tag as I don't need href. The click works fine by itself. But because I need to get the id of the parent which is the div with the class of weatherCard. At the moment when I click on other elements inside the card I cannot get the id because they are not the parent. The reason I need its id is when I get data with from the API I need a unique value for each card so that when you click on the card the data for that card will be shown in the other component which is the WeatherDetails component. But for now I need to be able to somehow choose that selected card and pull out the state for that unique card. Could someone help me out? Thanks.
You just need to pass the Parent component ID to your onClick function in Weather Card.
Here is your WeatherCard - Component
const WeatherCard = (props) => (
<div id={props.date} className="weatherCard" onClick={event => props.clicked(event, props.id)}>
<h2 className="cardDate">{props.date}</h2>
<h4>{props.forcast}</h4>
<div className="minmaxDeg">
<data>{props.min}</data>
<data>{props.max}</data>
</div>
<data>{props.rain}</data>
</div>
);
You can see that I have added props.id to your onClick function and with help of event now you can access that id from the parent component.
Now here is your Parent Component- WeatherCards
const WeatherCards = this.state.reports.map( (report, i) => {
return(
<WeatherCard
key={report.id}
id={i}
{...report}
clicked={this.handleCardClick}
/>
);
});
You can see in the code I am passing index number as id to your child component.
So this will give you an id (for now it's an index number) of the card in your onClick handler.
and Finally, here is your on click handler.
handleCardClick = (event, weatherCardID) => {
console.log(weatherCardID)
}
As of now, I am using the index as id if you want to use a unique identifier, you can change that easily.
General JavaScript solution is to differentiate the elements and .stopPropogation after you've captured the event you are targeting. A nested unordered list, <ul>would be an example. Tag the containing <li> with an .opened class upon rendering/displaying each level of nesting, tag those <li> elements accordingly, e.g. a dataset attribute such as data-make, then data-model, then data-option. You then attach and fire event listeners on the different level <li>'s.
Thank you #RutulPatel. I made your answer as the answer. But I changed your code a bit as I got your point so I wrote an answer as it is long. I think we might not need to change the WeatherCard at all and I don't pass event or any logic there. so it will be intact:
const WeatherCard = (props) => (
<div id={props.date} className="weatherCard" onClick={event => props.clicked(event, props.id)}>
<h2 className="cardDate">{props.date}</h2>
<h4>{props.forcast}</h4>
<div className="minmaxDeg">
<data>{props.min}</data>
<data>{props.max}</data>
</div>
<data>{props.rain}</data>
</div>
);
But I use your tip changing my weatherCards array to look like this:
const weatherCards = this.state.reports.map( report => {
return(
<WeatherCard
key={report.id}
id={report.date}
{...report}
clicked={() => this.handleCardClick(event, report.date)}
/>
);
});
So I use the report.date which is a unique value as my id. Also I don't pass event as a parameter to the arrow function I just pass it with the report.date to the handler:
clicked={() => this.handleCardClick(event, report.date)}
And the handler will be the same as you did:
handleCardClick = (event, weatherCardID) => {
console.log(weatherCardID)
}
I might even remove event later on from both if there was no need fo that.
Thank you again.
In react I have a code like this:
var myButtons=[];
/*Products is an array of objects where each object identify a product*/
for (var p of Products) {
var button = <button
style={someStyle}
onClick={onClickFunction}>
p.name
</button>
myButtons.push(button)
}
I will use this react array of buttons on a render command. The problem I have is that I do not know how to make one of these buttons to show its label p.name through the onClickFunction.
A simpler more user friendly way is to iterate the data with a function. (note that this does not take into account scope, so this may be needed if it's inside a component)
function makeButton(data) {
return (
<button
style={someStyle}
onClick={() => onClickFunction(data.label)}> //pass parameter for callback here if binding isn't used
data.name
</button>
);
}
Now you can simply use a binding map inside your div!
<div>
{Products.map(makeButton, this)}
</div>
You can add your label as paremeter :
<button style={someStyle} onClick={p.name => onClickFunction(p.name)}>
p.name
</button>
And :
onClickFunction = (label) => () =>{
console.log(label)
}
The easiest way is to use ES6 syntax and array map.
The name property should be unique, and don't forget provide a key for each button:
const myButtons = Products.map(p => (
<button style={someStyle} onClick={e=>{ this.onClickFunction(e, p.name); }} key={p.name}/>
{p.name}
</button>
));
Using an arrow function, so it doesn't require .bind(this). Add e.preventDefault() to prevent default behavior, if the buttons are in a form.
onClickFunction = (e, name) => {
e.preventDefault();
// Your button behavior goes here.
}