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.
}
Related
i want to improve my code, with several buttons that has custom class names (attr), when clicked should add to body tag (toggle), now is adding the first button only because for ("button")[0] but should work for each button
import React, { useState, useEffect } from "react"
function Test() {
const [isClass, setIsClass] = useState(false)
useEffect(() => {
const x = document.getElementsByTagName("button")[0].getAttribute("custom-class")
document.body.classList.toggle(x, isClass)
}, [isClass])
return (
<>
<button custom-class='test1' onClick={() => setIsClass(!isClass)}>
Setting test1 className
</button>
<button custom-class='test2' onClick={() => setIsClass(!isClass)}>
Setting test2 className
</button>
</>
)
}
export default Test
Thanks
Please use this code.
let oldStyle = "";
const handleClick = (index) => {
const x = [...document.getElementsByTagName("button")].map(value => value.getAttribute("custom-class"));
document.body.classList.contains(x[index]) ? document.body.classList.remove(x[index]) : document.body.classList.add(x[index]);
if(document.body.classList.length > 1) document.body.classList.replace(oldStyle, x[index]);
oldStyle = x[index];
}
return (
<>
<button custom-class='test1' onClick={() => handleClick(0)}>
Setting test1 className
</button>
<button custom-class='test2' onClick={() => handleClick(1)}>
Setting test2 className
</button>
</>
)
It is better not to use DOM querying and manipulation directly with elements that are created and controlled by react. In your particular example it is ok to use document.body, but not ok to search for buttons, especially when you try to find them by tag name. To actually toggle a class in classList you don't need second parameter in most cases, so additional state is also not needed.
React way to get reference to element renderend by React would be to use Ref. However, in your particular case side effect can be launched inside event handler, so you don't need useEffect or useRef.
Your onClick handler can accept event object that is Synthetic Event. It holds property target that holds reference to your button.
So, the easiest way would be simply to write like this:
function Test() {
function clickHandler(event) {
let classToToggle = event.target.getAttribute("custom-class");
document.body.classList.toggle(classToToggle);
}
return (
<>
<button key="test1" custom-class="test1" onClick={clickHandler}>
Setting test1 className
</button>
<button key="test2" custom-class="test2" onClick={clickHandler}>
Setting test2 className
</button>
</>
);
}
export default Test;
If you need to have only single className from the list, you can decide which class to enable or disable with a bit of a state. Since anything can add classes on body it might be useful to operate only on some set of classes and not remove everything.
Also, not mentioned before, but consider using data attribute as its purpose is to keep some additional data.
function Test() {
// this can come from props or be hardcoded depending on your requirements
// If you intend to change it in runtime, consider adding side effect to cleanup previous classes on body
let [classesList] = React.useState(["test1", "test2"]);
let [activeClass, setActiveClass] = React.useState("");
// You can switch actual classes in effect, if you want to
function clickHandler(event) {
let classToToggle = event.target.dataset.customClass;
// we remove all classes from body that are in our list
document.body.classList.remove(...classesList);
if (activeClass === classToToggle) {
setActiveClass("");
} else {
// if class not active - set new one
document.body.classList.add(classToToggle);
setActiveClass(classToToggle);
}
}
return (
<>
{classesList.map((cn) => (
<button key="cn" data-custom-class={cn} onClick={clickHandler}>
Setting {cn} className
</button>
))}
</>
);
}
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)}
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 am learning React. I have an array in my state which I am mapping through to render each value of the array on its own button. When the user clicks one of those buttons, I want to call a function to show some info specific to that button (the button represents a specific customer order). I tried to set state in the onClick event, but I got an 'Maximum update depth exceeded' error. If there is a way to set state from the onClick, that would solve my problem as I could just set the value of item of the particular button clicked to state and use it later. My other thought is to send the item to the showInfo function...however, I don't even know if that is possible. How can I call the same function from this list of buttons, and make the outcome unique to the button?
showInfo = (e) => {
}
componentDidMount() {
this.showAllOrders();
}
render() {
return (
<div>
<h1>Hello from past orders!!!</h1>
<ul id="orderList">
{this.state.orderDates.map((item =>
<li><button onClick={this.showInfo} key={item}>{item}</button></li>
))}
</ul>
</div>
)
}
}
You could provide the function showInfo with info about the button via params.
So that it would be onClick={() => this.showInfo(item)}
Use an inline arrow function as a callback to your onClick and pass the data you want.
...
showInfo = (item) => {
console.log(item);
}
...
render() {
return (
<div>
<h1>Hello from past orders!!!</h1>
<ul id="orderList">
{this.state.orderDates.map((item =>
<li><button onClick={() => this.showInfo(item)} key={item}>{item}</button></li>
))}
</ul>
</div>
)
Also, never setState in your render method and thats why you got max depth error in your earlier try.
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>