I was trying to pass both the event and some other data (the index of an element) to an event handler in a React App. I found here that I should make a higher-order function, but I don't get a pleasant result. Here is a snippet of my code:
const changeNameHandler = (index) => {
return (event) => {
console.log(index, event);
};
};
let persons = null;
if(showValuesState) {
persons = (
<div>
{
personsState.map((person, index) => {
return <Person
name = {person.name}
age = {person.age}
click = {() => deletePersonHandler(index)}
key = {index}
changeName = {() => changeNameHandler(index)()}/>
})
}
</div>
);
}
I am trying to pass the index to changeNameHandler function while also getting the event, but the value of the event variable from that console.log(index, event) statement is still undefined.
when ever you are calling a function from inside your jsx, if you wanted to pass extra values rather that the event, you have to consider doing like below:
changeName = {() => changeNameHandler(index)}/>
Also the important point is that you are getting the function hooked up on the changeName prop of your Person component but you don't capture the event value from it, so if you need to access the event value inside of your handler you have to do it like below:
changeName = {(evt) => changeNameHandler(evt, index)}/>
and by doing this you will get the event as first argument and he index value as second argument in your handler.
const changeNameHandler = (evt, index) => {
// do what ever you want here with values
}
Related
Below is the delete function which is working, it takes an argument and deletes the element with the index in the argument.
Setitem is a useState hook and is defined like this
when we click the on add list is added to item array
const listofitem = () => {
setitems((e) => {
return [...e, list];
});
};
This function is working.
const deleteItems = (e) => {
setitems((e1)=> {
return e1.filter( (er, index)=> {
return index!=e;
});
});
}
Why is this not working the splice method is working fine.
const deleteItems = (e) => {
items.splice(e-1, 1);
setitems((e1)=>{
return e1;
})
};
here is the code sandbox for the same, the function is working in this. But I want to know why the other is not working.
https://codesandbox.io/s/old-wood-cbnq86?file=/src/App.js:0-1169
This is how I got it to work, in your example you try to mutate the state directly and then not really set the state. If you want to use the state on setState you need to mutate what you call e1.
However you have some issues with your codesandbox that will trigger the function more than once sometimes
setitems((e1) => {
e1.splice(e, 1);
return [...e1];
});
Edit author updated question, this is not longer relevant.
In the first deleteItems function you never use e which I assume is the index you want to delete.
const deleteItems = (deleteIndex) => {
setitems((list)=> list.filter((_, index)=> index !== deleteIndex));
};
I am testing that myFunction is called when a button is clicked. myFunction actually just calls another function and passes some stuff to it, including the element that was clicked, which I expect to actually be the img inside the button.
E.g:
<button class="button" onClick=myFunction(someArgs)>
<img class="imageInsideButton" />
/>
myFunction = (someArgs) => (event) =>
this.props.someOtherFunction(
someArgs
event.target
);
In my test I am trying to check that someOtherFunction was called with the expected args, which includes event.target
function render(args, renderer = shallow) {
const component = renderer(<MyComponent {...args} />);
return {
component,
buttons: () => component.find(".button"),
imagesInsideButtons: () => component.find(".imageInsideButton"),
};
}
beforeEach(() => {
myProps = {
myFunction: jest.fn(),
};
});
it("Should call someOtherFunction with the correct args", () => {
const { buttons, imagesInsideButtons } = render(defaultArgs, mount);
const indexClicked = 1;
buttons().at(indexClicked).simulate("click");
expect(myProps.someOtherFunction).toHaveBeenCalledWith(
someArgs, imagesInsideButtons().at(indexClicked)
);
});
So in this test I am simulating a click of one of the buttons() (in this case the second one listed). I'm expecting the event target that was clicked to be the img that was wrapped inside this button.
This does sort of work, but the problem appears to be that my test expects a ReactComponent but instead gets an actual node:
Expected
the other args,
ReactWrapper {}
Received
the other args,
<button class="button"><img class"imageInsideButton" /></button>
It seems like the result is sort of OK, in terms of getting this event target, but the way in which I have written the test means these are not matching. How do I make this test work?
When simulating the click, you could pass a fake target, and expect THAT target.
buttons().at(indexClicked).simulate("click", { target: 999 });
// test
expect(myProps.someOtherFunction).toHaveBeenCalledWith(
someArgs, 999
);
I would like to ask you about using a event function in React.js.
I want to make test function, which would get index and print index when of titlesList is clicked.
But this function doesn't work when is clicked.
Could you give me advices to solve it?
const some = (props) = {
// 'props' have two attributes, titles and contents
// which each is a array of strings.
function test(index){
console.log('clicked');
console.log(index);
}
const titlesList = props.titles.map((title, index) => {
return <div className="eachTitle"
key={index}
onClick={test(index)}>
{title} {index}
</div>
});
return (
<div>
{titlesList}
</div>
);
}
Thank you for reading.
When your component is rendered, it will actually call test(index). This sets the value of onClick to the return value of test(index). What you'll want to do is set onClick to a function that calls whatever you want with the proper arguments.
onClick={() => {test(index)}}
This is an anonymous function, which can be passed around. When clicked, the anonymous function is called, which really just calls test(index) with your arguments. If you didn't need to pass any argument to test, you could have just done:
onClick={test}.
Since you can't tell onClick to pass arguments (besides the event object), the anonymous function is an easy way to get around that.
The problem here is with the binding, there are two approach to resolve it, one mentioned by #Jtcruthers using anonymous function and another way is to create a constructor and register you method using .bind() while calling use this
onClick={this.test(index)}
constructor(props){
super(props);
this.test = this.test.bind(this);
}
function test(index){
console.log('clicked');
console.log(index);
}
const titlesList = props.titles.map((title, index) => {
return <div className="eachTitle"
key={index}
onClick={this.test(index)}>
{title} {index}
</div>
});
In my react component, orders[i] is defined, since the component is rendered using name and amount, however, when it comes to onClick event, orders[i] is undefined in the context of the arrow function. How can I call that function using orders[i].name as an argument?
let orders = this.state.orders;
let ordersRender =[];
for (var i=0; i<orders.length; i++) {
if (orders[i].amount) {
let newInvoiceItem = <InvoiceItem name={orders[i].name} amount ={orders[i].amount} key={i} handleDelete={() => this.deleteProduct(orders[i].name)}/>;
ordersRender.push(newInvoiceItem);
}
}
When handleDelete event is fired, the variable i has changed its value to orders.length, that's why orders[i] is undefined when onClick is fired.
So, instead of for-loop, use Array#map to iterate over the orders and return an array of InvoiceItem items. Since amount is optional, we need to filter the orders based on the amount before applying the .map function.
let { orders } = this.state;
let ordersRender = orders.filter(order => order.amount).map(({name, amount}, index) => {
return (
<InvoiceItem name={name} amount ={amount} key={index} handleDelete={() => this.deleteProduct(name)}/>
);
})
...
Hope this will help!
Simply changing var to let solves this issue.
Without Parameter
function clickMe(e){
//e is the event
}
<button onClick={this.clickMe}></button>
With Parameter
function clickMe(parameter){
//how to get the "e" ?
}
<button onClick={() => this.clickMe(someparameter)}></button>
I want to get the event. How can I get it?
Try this:
<button
onClick={(e) => {
this.clickMe(e, someParameter);
}}
>
Click Me!
</button>
And in your function:
function clickMe(event, someParameter){
//do with event
}
With the ES6, you can do in a shorter way like this:
const clickMe = (parameter) => (event) => {
// Do something
}
And use it:
<button onClick={clickMe(someParameter)} />
Solution 1
function clickMe(parameter, event){
}
<button onClick={(event) => {this.clickMe(someparameter, event)}></button>
Solution 2
Using the bind function is considered better, than the arrow function way, in solution 1.
Note, that the event parameter should be the last parameter in the handler function
function clickMe(parameter, event){
}
<button onClick={this.clickMe.bind(this, someParameter)}></button>
Currying with ES6 example:
const clickHandler = param => event => {
console.log(param); // your parameter
console.log(event.type); // event type, e.g.: click, etc.
};
Our button, that toggles handler:
<button onClick={(e) => clickHandler(1)(e)}>Click me!</button>
If you want to call this function expression without an event object, then you'd call it this way:
clickHandler(1)();
Also, since react uses synthetic events (a wrapper for native events), there's an event pooling thing, which means, if you want to use your event object asynchronously, then you'd have to use event.persist():
const clickHandler = param => event => {
event.persist();
console.log(event.target);
setTimeout(() => console.log(event.target), 1000); // won't be null, otherwise if you haven't used event.persist() it would be null.
};
Here's live example: https://codesandbox.io/s/compassionate-joliot-4eblc?fontsize=14&hidenavigation=1&theme=dark
To solve the creating new callback issue completely, utilize the data-* attributes in HTML5 is the best solution IMO.
Since in the end of the day, even if you extract a sub-component to pass the parameters, it still creates new functions.
For example,
const handleBtnClick = e => {
const { id } = JSON.parse(e.target.dataset.onclickparam);
// ...
};
<button onClick={handleBtnClick} data-onclickparam={JSON.stringify({ id: 0 })}>
See https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes for using data-* attributes.
<Button onClick={(e)=>(console.log(e)}>Click Me</Button>