Passing parameter in ReactJS onclick - javascript

I have this React component that has items generated from a list (with a map function). Each of those elements has a button. I want this button's onclick button to pass in a parameter to identify which list item's button was clicked.
It looks something like this.
var Component = React.createClass({
assignItem: function(item){
this.setState({item:item})
},
render: function(){
var listItems = list.map(function(item){
return <div>{item}
<button onClick={this.assignItem(item)>Click</button>
</div>
})
return <div>{listItems}</div>
}
})
Of course that doesn't work. The error message says that this.assignItem is not a function.
I know the official React documentation suggests this:
var handleClick = function(i, props) {
console.log('You clicked: ' + props.items[i]);
}
function GroceryList(props) {
return (
<div>
{props.items.map(function(item, i) {
return (
<div onClick={handleClick.bind(this, i, props)} key={i}>{item}</div>
);
})}
</div>
);
}
ReactDOM.render(
<GroceryList items={['Apple', 'Banana', 'Cranberry']} />, mountNode
);
but that works with a function outside of the component. Since I want my function to manipulate the sate, I want to keep it within the React component.
How do I do this?

Original accepted answer:
You can bind to a function on this just like the React example, however since you are rendering in a map callback you need to either pass in thisArg or use a fat arrow function:
var Component = React.createClass({
assignItem: function(item){
this.setState({item:item})
},
render: function(){
// bind to this.assignItem
var listItems = list.map(function(item){
return <div>{item}
<button onClick={this.assignItem.bind(this, item)}>Click</button>
</div>
}, this); // pass in this, or use fat arrow map callback
return <div>{listItems}</div>
}
})
Update 2017:
This is an old question using an old React API and an accordingly old answer. Today you should be using the class or functional React component API. For passing arguments to click handlers you can just write an inline fat arrow function and call through with whatever params you want. The above example ends up like this:
class MyComponent extends React.Component { // or React.PureComponent
assignItem = item => { // bound arrow function handler
this.setState({ item: item });
}
render() {
var listItems = list.map(item => {
// onClick is an arrow function that calls this.assignItem
return <div>{item}
<button onClick={e => this.assignItem(item)}>Click</button>
</div>
});
return <div>{ listItems }</div>
}
}
Note: The assignItem handler must be bound, which is done here using an arrow function as a class property.

Related

onSubmit function in React renders and calls the function again infinitely [duplicate]

How to pass extra parameters to an onClick event using the ES6 syntax?
For instance:
handleRemove = (e) => {
}
render() {
<button onClick={this.handleRemove}></button>
}
I want to pass an id to the handleRemove function like this:
<button onClick={this.handleRemove(id)}></button>
Remember that in onClick={ ... }, the ... is a JavaScript expression. So
... onClick={this.handleRemove(id)}
is the same as
var val = this.handleRemove(id);
... onClick={val}
In other words, you call this.handleRemove(id) immediately, and pass that value to onClick, which isn't what you want.
Instead, you want to create a new function with one of the arguments already prefilled; essentially, you want the following:
var newFn = function() {
var args = Array.prototype.slice.call(arguments);
// args[0] contains the event object
this.handleRemove.apply(this, [id].concat(args));
}
... onClick={newFn}
There is a way to express this in ES5 JavaScript: Function.prototype.bind.
... onClick={this.handleRemove.bind(this, id)}
If you use React.createClass, React automatically binds this for you on instance methods, and it may complain unless you change it to this.handleRemove.bind(null, id).
You can also simply define the function inline; this is made shorter with arrow functions if your environment or transpiler supports them:
... onClick={() => this.handleRemove(id)}
If you need access to the event, you can just pass it along:
... onClick={(evt) => this.handleRemove(id, evt)}
Use the value attribute of the button element to pass the id, as
<button onClick={this.handleRemove} value={id}>Remove</button>
and then in handleRemove, read the value from event as:
handleRemove(event) {
...
remove(event.target.value);
...
}
This way you avoid creating a new function (when compared to using an arrow function) every time this component is re-rendered.
Use Arrow function like this:
<button onClick={()=>{this.handleRemove(id)}}></button>
onClick={this.handleRemove.bind(this, id)}
Using with arrow function
onClick={()=>{this.handleRemove(id)}}
Something nobody has mentioned so far is to make handleRemove return a function.
You can do something like:
handleRemove = id => event => {
// Do stuff with id and event
}
// render...
return <button onClick={this.handleRemove(id)} />
However all of these solutions have the downside of creating a new function on each render. Better to create a new component for Button which gets passed the id and the handleRemove separately.
TL;DR:
Don't bind function (nor use arrow functions) inside render method. See official recommendations.
https://reactjs.org/docs/faq-functions.html
So, there's an accepted answer and a couple more that points the same. And also there are some comments preventing people from using bind within the render method, and also avoiding arrow functions there for the same reason (those functions will be created once again and again on each render). But there's no example, so I'm writing one.
Basically, you have to bind your functions in the constructor.
class Actions extends Component {
static propTypes = {
entity_id: PropTypes.number,
contact_id: PropTypes.number,
onReplace: PropTypes.func.isRequired,
onTransfer: PropTypes.func.isRequired
}
constructor() {
super();
this.onReplace = this.onReplace.bind(this);
this.onTransfer = this.onTransfer.bind(this);
}
onReplace() {
this.props.onReplace(this.props.entity_id, this.props.contact_id);
}
onTransfer() {
this.props.onTransfer(this.props.entity_id, this.props.contact_id);
}
render() {
return (
<div className="actions">
<button className="btn btn-circle btn-icon-only btn-default"
onClick={this.onReplace}
title="Replace">
<i className="fa fa-refresh"></i>
</button>
<button className="btn btn-circle btn-icon-only btn-default"
onClick={this.onTransfer}
title="Transfer">
<i className="fa fa-share"></i>
</button>
</div>
)
}
}
export default Actions
Key lines are:
constructor
this.onReplace = this.onReplace.bind(this);
method
onReplace() {
this.props.onReplace(this.props.entity_id, this.props.contact_id);
}
render
onClick={this.onReplace}
in function component, this works great - a new React user since 2020 :)
handleRemove = (e, id) => {
//removeById(id);
}
return(<button onClick={(e)=> handleRemove(e, id)}></button> )
I use the following code:
<Button onClick={this.onSubmit} id={item.key} value={shop.ethereum}>
Approve
</Button>
Then inside the method:
onSubmit = async event => {
event.preventDefault();
event.persist();
console.log("Param passed => Eth addrs: ", event.target.value)
console.log("Param passed => id: ", event.target.id)
...
}
As a result:
Param passed in event => Eth addrs: 0x4D86c35fdC080Ce449E89C6BC058E6cc4a4D49A6
Param passed in event => id: Mlz4OTBSwcgPLBzVZ7BQbwVjGip1
I am using React-Bootstrap. The onSelect trigger for dropdowns were not allowing me to pass data. Just the event. So remember you can just set any values as attributes and pick them up from the function using javascript. Picking up those attributes you set in that event target.
let currentTarget = event.target;
let currentId = currentTarget.getAttribute('data-id');
let currentValue = currentTarget.getAttribute('data-value');

Why do i need to bind this arrow function in scope of map?

I'm trying to display a array from an array object using onClick event on a li , i am using arrow function but i found out i needed to bind the function inside the scope of map , why is that? Isn't that the purpose of arrow function to eliminate the need of binding?
class ListRecipes extends React.Component {
showIngredients = (item) => {
console.log(item.ingredients)
}
render() {
const { list } = this.props;
let Recipe = list.map((item, index) => {
let boundClick = this.showIngredients.bind(this, item);
return <li key={index} onClick={boundClick}> {item.recipeName} </li>
})
return (
<div>
<ul>
{Recipe}
</ul>
</div>
)
}
}
Also what is the difference between this code that returns a new function from the above?
class ListRecipes extends React.Component {
showIngredients = (item) => (event) => {
console.log(item.ingredients)
}
render() {
const { list } = this.props;
let Recipe = list.map((item, index) => {
return <li key={index} onClick={this.showIngredients(item)}> {item.recipeName} </li>
})
return (
<div>
<ul>
{Recipe}
</ul>
</div>
)
}
}
When you write onClick={this.showIngredients(item)} you are not assigning a reference of function to onClick but calling it
You could change it to
let Recipe = list.map((item, index) => {
return <li key={index} onClick={() => this.showIngredients(item)}> {item.recipeName} </li>
})
Also with let boundClick = this.showIngredients.bind(this, item); you are binding the showIngredients function to the context and passing an extra argument to it and since bind returns a new function so , boundClick is a function which is then later assigned to onClick
I think this code is generally preferred:
let boundClick = this.showIngredients.bind(this, item);
You are binding the item to be passed to the showIngredients function. So you end up creating items.length number of separate function references, each bound to this and the current item.
Before anything else we need to understand that the passed argument in callback listener on onClick is event.
That mean
function myMethod(event) {
// only event is passed as argument
}
<div onClick={myMehtod}/>
So question is how can you pass additional argument then? Simple. Call your desired method from the listener.
function calculate(item){
// no event trace here
}
<div onClick={function(event) { calculate(item) }}/>
// OR better, fat arrow
<div onClick={event => calculate(item)}/>
Remember the fat arrow is always bind to this scope rather then to prototype.
class Component {
scopedMethod = () => {
this.name = 'John';
}
prototypeMethod() {
this.name = 'John';
}
render() {
return (
<div>
{/* works */}
<button onClick={this.scopedMethod}/>
{/* OOPS, 'this' is event rather then component */}
<button onClick={this.prototypeMethod}/>
{/* works */}
<button onClick={() => this.prototypeMethod()}/>
{/* works */}
<button onClick={this.prototypeMethod.bind(this)}/>
</div>
)
}
}
Hopefully that's brought a light in. So the question would be when and why I would to use scope binded method over prototype method and vice versa? Well, prototype methods are cheap. Always try to use them. However some developer are avoiding lambda function (generate function inside render) because it can cause performance issue. When you need to re-render your component 10x in second, then the new lambda is created 10x.

Is there a way to assign a custom object property to an element?

I'm using react 16.0.
I want to assign a custom object property to an element and get its value.
It is as follows. (https://jsfiddle.net/69z2wepo/96660/) Of course it does not work.
class Test extends React.PureComponent {
render () {
let numbers = { number:1, number2:2, number3:3 };
return <div numbers={numbers} onClick={(event) => console.log(event.target.numbers.number)}>Test</div>;
}
}
ReactDOM.render(
<Test/>, document.querySelector('body')
);
I want to know if there is a good way. Thanks.
If you only want to access that value inside onClick handler then you can bind that value with the action handler itself
const handler = (numbers, e) => {
console.log(numbers)
}
render () {
let numbers = { number:1, number2:2, number3:3 };
return <div onClick={this.handler.bind(this, numbers)}>Test</div>;
}

Create function that can pass a parameter without making a new component

My question is to do with the issue React has for binding functions in the render function.
The following is not good practice:
render() {
<div onClick={this.callFunction.bind(this)}/>
}
as each re render would add a new function to the page, eventually causing the browser to run out of memory.
The solution is to do this:
constructor() {
this.callFunction = this.callFunction.bind(this);
}
render() {
<div onClick={this.callFunction}/>
}
The problem with this is when I want to pass a value into the function.
I know I can make the div a child component, and pass the parameter in through the callBack, but this does not seem sensible if the div is only being used once in the whole application. I accept I could make this work, but this is not the scope of this question.
I also know that this solution:
render() {
<div onClick={() => this.callFunction.call(this, param)}/>
}
Is no better, as it is still creating a new function.
So my question is, how can I create a function that I can pass a parameter into without making a new component, and without binding a new funciton on each render?
You can't avoid creating a second component as you need to pass a function reference as an event handler, this will be executed by the browser when the event triggers.
So the problem is not the binding but the fact that you need to pass a reference, and references can't receive parameters.
EDIT
By the way, if you don't like the syntax and noise of binding or anonymous arrow functions you can use currying.
I posted an example in a different question if you find it interesting. this won't solve the problem though, it's just another approach to pass a new reference (which i find it to be the most terse)
You can change the declaration of callFunction to be an arrow function, which implictly binds the scope, like so:
callFunction = () => {
console.log('hi');
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Then your original render function would work as expected!
Use Side effect
Side effect is something that a function use that comes from outside but not as argument. Now this mechanism is majorly used in Redux/Flux where the entire state is stored in a Store and every component fetches their state from it.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
handlerProps: {
onClick: { count: 0},
onChange: { count: 0}
}
}
}
onClickHandler = () => {
const state = this.state.handlerProps.onClick;
console.log('onClick', state.count);
}
onChangeHandler = (value) => {
const state = this.state.handlerProps.onChange;
console.log('onClick', state.count);
this.setState({value: value})
}
buttonClick = () => {
const random = Math.ceil(Math.random()* 10) % 2;
const handler = ['onClick', 'onChange'][random];
const state = this.state.handlerProps;
state[handler].count++;
console.log('Changing for event: ', handler);
this.setState({handlerProps: state});
}
render () {
return (
<div>
<input onClick={this.onClickHandler} onChange={this.onChangeHandler} />
<button onClick={ this.buttonClick }>Update Props</button>
</div>
)
}
}
ReactDOM.render(<MyComponent/>, document.querySelector('.content'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
<div class='content' />
The only way I know of is to create a new React Component which takes the value and the event handler as props.
This way, the handler as a function remains static, and since the value is passed down separately (in its own prop) you don't have any functions being re-instanciated. Because you don't bind anything nor create a new function each time.
Here's an example:
We have two buttons. The first one prints the current state variable value and the other increments it by one.
Normally, if we had done this with onClick={() => this.print(this.state.value)} we would get a new instance of this function, each time the MyApp component would re-render. In this case, it would re-render each time we increment the value with the setState() inside this.increment.
However, in this example, no new instance of this.print happens because we are only passing its reference to the button. In other words, no fat arrow and no binding.
In the <Button /> component, we have a <button> to which event handler we pass a reference to a function - just like we did in <MyApp />. However, here we know exactly what to pass to the function. As such, we have myHandler trigger this.props.handler(this.props.value).
class MyApp extends React.Component {
constructor() {
super();
this.state = {
value: 0
};
}
print = (value) => {
console.log(value);
}
increment = () => {
// This will trigger a re-render, but none of the functions will be reinstanciated!
this.setState((prevState) => ({value: prevState.value + 1}));
}
render() {
// Note that both handlers below are passed just the references to functions. No "bind" and no fat arrow.
return(
<div>
<Button handler={this.print} value={this.state.value}>Print</Button>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
class Button extends React.Component {
// Clicking the button will trigger this function, which in turn triggers the function with the known argument - both of which were passed down via props.
myHandler = () => this.props.handler(this.props.value);
render() {
// Note again that the handler below is just given the reference to a function. Again, not "bind" nor fat arrow.
return(
<button onClick={this.myHandler}>{this.props.children}</button>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Though quite tedious, it is an effective solution. That being said, even if you do create a new function each time you render, the performance implications are minimal. From the official docs:
The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine.

no argument props method does not get called from child component in react

I am new to React and js I need some clarifications on binding of methods in components
I have 2 components ParentComponent and ChildComponent
Parent
var ParentComponent = React.createClass({
methodArg: function (value) {
console.log("methodArg called", value);
},
methodNoArg: function () {
console.log("methodNoArg called");
},
render: function () {
return <ChildComponent m1={(value) => this.methodArg(value)} m2={() => this.methodNoArg} />
}
})
Child
var ChildComponent = React.createClass({
render: function () {
return (
<div>
<button onClick={()=>this.props.m1(100)}>Call M1</button>
<button onClick={()=>this.props.m2()}>Call M2</button>
</div>
)
}
})
When i click Call M1 button, methodArg() of parent is getting called.
But when I click Call M2 methodNoArg() is not getting called. What is the issue in this ?
When I pass methodNoArg to Child , it is getting called
<ChildComponent m1={this.methodArg()} m2={this.methodNoArg} />
But methodArg() is getting called without clicking the button , its getting called everytime when the child component is rendered.
<button onClick={()=>this.props.m1(100)}>Call M1</button>
Your above line is saying evaluate the m1 method and assign the result to onClick. So when you refresh your page you console.log statement with value you passed gets printed but it will never gets called again no matter how many times you click the button as onClick now does not have method assigned to it now.
You can achieve what you want by removing that parenthesis which is calling the method automatically without clicking.
Here is working jsfiddle code link :
http://jsfiddle.net/fp0LvkLg/
This is because of the way you assign your method to a prop
m2={() => this.methodNoArg}
is (kind of if we omit the intricacies of this) equivalent to
m2={function() {return this.methodNoArg}}
So your prop is a function, which returns a function, which in turn is never called.
You want to simply assign your function to a prop like this:
m2={this.methodNoArg}

Categories

Resources