893

I want to read the onClick event value properties. But when I click on it, I see something like this on the console:

SyntheticMouseEvent {dispatchConfig: Object, dispatchMarker: ".1.1.0.2.0.0:1", nativeEvent: MouseEvent, type: "click", target

My code is working correctly. When I run I can see {column} but can't get it in the onClick event.

My Code:

var HeaderRows = React.createClass({
  handleSort:  function(value) {
    console.log(value);
  },
  render: function () {
    var that = this;
    return(
      <tr>
        {this.props.defaultColumns.map(function (column) {
          return (
            <th value={column} onClick={that.handleSort} >{column}</th>
          );
        })}
        {this.props.externalColumns.map(function (column) {
          // Multi dimension array - 0 is column name
          var externalColumnName = column[0];
          return ( <th>{externalColumnName}</th>);
        })}
      </tr>
    );
  }
});

How can I pass a value to the onClick event in React js?

JGallardo
  • 11,074
  • 10
  • 82
  • 96
user1924375
  • 10,581
  • 6
  • 20
  • 27
  • 2
    possible duplicate of [OnClick Event binding in React.js](http://stackoverflow.com/questions/27397266/onclick-event-binding-in-react-js) – WiredPrairie Apr 23 '15 at 00:11
  • 2
    consider using self instead of that. That is fairly misleading as it should be synonymous with "this" (not important really though, to each his own) – Dan Jun 30 '17 at 14:53
  • Using bind method and arrow method we can pass the value to Onclick event – Merugu Prashanth Sep 12 '17 at 07:49
  • Agreed with @WiredPrairie and the guy named ZenMaster explained precisely and effectively. – perfectionist1 Feb 24 '21 at 14:14
  • What you are doing wrong there is passing the return value of the call to handleSort and of course, it happens at page load – Ojage S Feb 16 '22 at 08:14
  • The listener is a function reference or an anonymous function itself, so instead use an arrow function – Ojage S Feb 16 '22 at 08:15

37 Answers37

1637

Easy Way

Use an arrow function:

return (
  <th value={column} onClick={() => this.handleSort(column)}>{column}</th>
);

This will create a new function that calls handleSort with the right params.

Better Way

Extract it into a sub-component. The problem with using an arrow function in the render call is it will create a new function every time, which ends up causing unneeded re-renders.

If you create a sub-component, you can pass handler and use props as the arguments, which will then re-render only when the props change (because the handler reference now never changes):

Sub-component

class TableHeader extends Component {
  handleClick = () => {
    this.props.onHeaderClick(this.props.value);
  }

  render() {
    return (
      <th onClick={this.handleClick}>
        {this.props.column}
      </th>
    );
  }
}

Main component

{this.props.defaultColumns.map((column) => (
  <TableHeader
    value={column}
    onHeaderClick={this.handleSort}
  />
))}

Old Easy Way (ES5)

Use .bind to pass the parameter you want, this way you are binding the function with the Component context :

return (
  <th value={column} onClick={this.handleSort.bind(this, column)}>{column}</th>
);
theSereneRebel
  • 196
  • 3
  • 16
Austin Greco
  • 32,997
  • 6
  • 55
  • 59
  • 9
    react gives warning when i use like your code. I change my code to onClick={that.onClick.bind(null,column)} – user1924375 Apr 23 '15 at 09:13
  • do you have the official doc reference for that function? I can't find it too many libraries are found for the `bind` keyword... – Bruno Aug 11 '15 at 02:25
  • 2
    `bind` is part of regular javascript (ES5) -- check out the docs for [`.bind` on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) – Austin Greco Aug 12 '15 at 09:02
  • 13
  • 41
    @SimonH The event will be passed as the last argument, after the arguments you pass via `bind`. – smudge Nov 04 '15 at 17:26
  • 49
    Is this not bad for performance? wont a new function be created on each render? – AndrewMcLagan May 17 '16 at 07:44
  • 14
    @AndrewMcLagan It is. I [found this](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md) to describe the rule and the most performant solution. – E. Sundin Jul 03 '16 at 22:46
  • @E.Sundin How do you pass a dynamic argument to the function using that approach? – Tom Raganowicz Nov 04 '16 at 08:35
  • 1
    @NeverEndingQueue Based on the section in that article "Protips - List of Items", you pass whatever you want as an argument down as a prop, then have the function pass it back up. It may seem cumbersome, but that is the most efficient way performance-wise. Or, just take the performance hit and use bind. – aaronofleonard Mar 02 '17 at 22:27
  • 7
    This answer is totally wrong. If you use it you will get new function reference inside onClick={() => this.handleSort(column)} every time and each render DOM will be different and react will have to to update it. It breaks React reconciliation. – Alex Shwarc Apr 11 '17 at 16:37
  • 5
    @AlexShwarc yes, that's why there is the "better way" section of the answer, that does not create a new function every render :) – Félix Adriyel Gagnon-Grenier May 14 '18 at 17:39
  • dont use `bind`. it returns a new function every time render is called. it is same as creating a anonymous function which is created everytime render gets called – hannad rehman Sep 16 '18 at 14:05
  • Binding functions on render is a bad decision! – msqar Nov 25 '19 at 16:04
  • or use `useCallBack` with the first method and it wont create a new function ever time! – callback Nov 25 '21 at 07:38
  • Any reason why you say that the first way is not a good way compared to the rest 2? – Bidisha Das Jan 04 '22 at 18:19
  • New to JS, why do we need to pass an arrow function? Why is passing "this.handleSort(column)" directly not allowed? Why does it need to be wrapped in an arrow function? – Jiwa Chhetri Feb 03 '22 at 11:55
212

There are nice answers here, and i agree with @Austin Greco (the second option with separate components)
There is another way i like, currying.
What you can do is create a function that accept a parameter (your parameter) and returns another function that accepts another parameter (the click event in this case). then you are free to do with it what ever you want.

ES5:

handleChange(param) { // param is the argument you passed to the function
    return function (e) { // e is the event object that returned

    };
}

ES6:

handleChange = param => e => {
    // param is the argument you passed to the function
    // e is the event object that returned
};

And you will use it this way:

<input 
    type="text" 
    onChange={this.handleChange(someParam)} 
/>

Here is a full example of such usage:

const someArr = ["A", "B", "C", "D"];

class App extends React.Component {
  state = {
    valueA: "",
    valueB: "some initial value",
    valueC: "",
    valueD: "blah blah"
  };

  handleChange = param => e => {
    const nextValue = e.target.value;
    this.setState({ ["value" + param]: nextValue });
  };

  render() {
    return (
      <div>
        {someArr.map(obj => {
          return (
            <div>
              <label>
                {`input ${obj}   `}
              </label>
              <input
                type="text"
                value={this.state["value" + obj]}
                onChange={this.handleChange(obj)}
              />
              <br />
              <br />
            </div>
          );
        })}
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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="root"></div>

Note that this approach doesn't solve the creation of a new instance on each render.
I like this approach over the other inline handlers as this one is more concise and readable in my opinion.

Edit:
As suggested in the comments below, you can cache / memoize the result of the function.

Here is a naive implementation:

let memo = {};

const someArr = ["A", "B", "C", "D"];

class App extends React.Component {
  state = {
    valueA: "",
    valueB: "some initial value",
    valueC: "",
    valueD: "blah blah"
  };

  handleChange = param => {
    const handler = e => {
      const nextValue = e.target.value;
      this.setState({ ["value" + param]: nextValue });
    }
    if (!memo[param]) {
      memo[param] = e => handler(e)
    }
    return memo[param]
  };

  render() {
    return (
      <div>
        {someArr.map(obj => {
          return (
            <div key={obj}>
              <label>
                {`input ${obj}   `}
              </label>
              <input
                type="text"
                value={this.state["value" + obj]}
                onChange={this.handleChange(obj)}
              />
              <br />
              <br />
            </div>
          );
        })}
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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="root" />
Sagiv b.g
  • 30,379
  • 9
  • 68
  • 99
  • 35
    Looks better, but from a performance perspective though, currying doesn't really help, because if you call handleChange twice, with the same param, you get two functions that the JS engine consider to be separate objects, even if they do the same thing. So you still get a re-render. For the sake of performance, you would need to cache the results of handleChange to get the performance advantage. Like `handleChange = param => cache[param] || (e => { // the function body })` – travellingprog Aug 26 '17 at 01:38
  • 3
    Can anyone provide a link or explain how this caching mechanism works ? thanks. – Sadok Mtir Jan 26 '18 at 16:02
  • Beautiful. One doubt, how to make the parameter optional in ES6 way.? – Jithesh Kt Mar 08 '18 at 02:19
  • @JitheshKt if by optional you mean default then you can simply do this: `handleChange = (param = 'something') => e => {` – Sagiv b.g Mar 08 '18 at 05:26
  • 2
    Yes, currying is the way to go. I agree with @Tamb, it should be the accepted answer. You can read more on the topic in this article: [Parameterized Event Handlers](https://medium.freecodecamp.org/reactjs-pass-parameters-to-event-handlers-ca1f5c422b9) – cutemachine Aug 14 '18 at 05:47
  • @SadokMtir i've added an example for memoization – Sagiv b.g Oct 05 '18 at 18:27
  • 4
    If you use currying, new function will be created at each render. Same performance problem occurs as passing an arrow function. – omeralper Feb 20 '19 at 23:58
  • @omeralper its mentioned in the answer – Sagiv b.g Feb 21 '19 at 05:38
  • 1
    @travellingprog Is that what is called memoization? – Ari Jul 09 '19 at 00:47
  • @JitheshKt, yes it is possible by implementing conditional statment. By the way, this answer (Sagivb.g) would be as an accepted answer. – perfectionist1 Feb 24 '21 at 14:54
142

Nowadays, with ES6, I feel we could use an updated answer.

return (
  <th value={column} onClick={()=>this.handleSort(column)} >{column}</th>
);

Basically, (for any that don't know) since onClick is expecting a function passed to it, bind works because it creates a copy of a function. Instead we can pass an arrow function expression that simply invokes the function we want, and preserves this. You should never need to bind the render method in React, but if for some reason you're losing this in one of your component methods:

constructor(props) {
  super(props);
  this.myMethod = this.myMethod.bind(this);
}
aikeru
  • 3,773
  • 3
  • 33
  • 48
  • 11
    You never need to bind `render()` because it is called by React. If anything, you need to `bind` the event handlers, and only when you are not using arrows. – Dan Abramov Mar 02 '16 at 13:46
  • 1
    @DanAbramov I think you are correct, but I included it just in case - updated with an example that doesn't implicitly encourage binding render. – aikeru Mar 02 '16 at 13:50
  • 4
    Note it’s also best to pass `props` to `super()` or `this.props` will be undefined during constructor which can get confusing. – Dan Abramov Mar 02 '16 at 13:55
  • 1
    This approach is recommended by the official React docs https://facebook.github.io/react/docs/reusable-components.html – Petr Peller Apr 02 '16 at 12:00
  • @DanAbramov - Is it possible to achieve the same with stateless function? (or is it a bad practice to define a method inside a stateless function altogether?) – Erez Cohen Apr 19 '16 at 19:51
  • 3
    To achieve what? You can define a handler inside functional component and pass it. It will be a different function every render so *if you have determined with a profiler that it gives you perf issues* (and only if you’re actually sure in this!), consider using a class instead. – Dan Abramov Apr 19 '16 at 23:04
  • What's the difference between this and @krotov solution. Doesn't this do the same in creating a new function every time the click happens ? – me-me May 24 '18 at 04:33
  • 1
    @me-me Yes, and if you opt-in to bleeding edge JavaScript in Babel you should just declare properties on your class like `foo = () => {...}` and then reference in render ` – aikeru May 24 '18 at 17:14
  • 2
    Thanks Aikeru. Ah ok so you are saying if you declare the foo method as an arrow function on the class itself, then reference it using the this context then its only created once instead of declaring it inside the onClick where its creating the same function every time you click the button. Is that correct in my thinking. – me-me May 25 '18 at 15:43
  • Note that using an arrow function like this to call a handler and passing it a variable will fire on render, and not on click, which negates any point in terms of having something happen ON click and not on render (meaning, whatever logic you're doing on the click, will be fired immediately) – zuckerburg May 31 '19 at 09:55
  • 2
    @zuckerburg onClick={() => alert('hi')} will not immediately alert, nor will onClick={this.showAlert} because in both cases you are passing (not invoking) a function. – aikeru Jun 07 '19 at 15:37
81

[[h/t to @E.Sundin for linking this in a comment]

The top answer (anonymous functions or binding) will work, but it's not the most performant, as it creates a copy of the event handler for every instance generated by the map() function.

This is an explanation of the optimal way to do it from the ESLint-plugin-react:

Lists of Items

A common use case of bind in render is when rendering a list, to have a separate callback per list item:

const List = props => (
      <ul>
        {props.items.map(item =>
          <li key={item.id} onClick={() => console.log(item.id)}>
            ...
          </li>
        )}
      </ul>
    );

Rather than doing it this way, pull the repeated section into its own component:

const List = props => (
      <ul>
        {props.items.map(item =>
          <ListItem 
            key={item.id} 
            item={item} 
            onItemClick={props.onItemClick} // assume this is passed down to List
           />
        )}
      </ul>
    );


const ListItem = props => {
  const _onClick = () => {
    console.log(props.item.id);
  }
    return (
      <li onClick={_onClick}>
        ...
      </li>
    );

});

This will speed up rendering, as it avoids the need to create new functions (through bind calls) on every render.

Brandon
  • 7,736
  • 9
  • 47
  • 72
  • Does react invoke those functions with call/apply, then, under the hood, and avoid using bind internally? – aikeru Aug 12 '16 at 15:07
  • 1
    Is there a way to doing this using a stateless component? – Carlos Martinez May 09 '17 at 13:50
  • 2
    @CarlosMartinez good eye, i updated the example--they should've been stateless functional components (SFC) in the first place. Generally, if a component doesn't ever use `this.state`, you can safely swap it out with an SFC. – Brandon May 09 '17 at 14:46
  • 4
    Hmm, I don't get how this is more performant? Won't the ListItem function be invoked every time, and thus the _onClick function will be created every render. – Mattias Petter Johansson May 19 '17 at 21:04
  • 1
    I'm far from an expert here, but as I understand it, in the 'correct' pattern, there's only one instance of the handler and it's passed the prop for whichever instance of the component calls it. In the bind example (ie, the 'wrong' pattern), there's one instance of the handler for every instantiated component. It's sort of the memory equivalent of writing the same function thirty times vice writing it once & calling it where needed. – Brandon May 19 '17 at 21:27
  • Isn't it just rendering more functions? – Ari Jul 09 '19 at 00:07
  • Did you mean to use `props.onItemClick` instead of `console.log` – Taylan Nov 08 '19 at 19:08
  • How can we test this to ensure that the callback is not being created on every render? – Isaac Pak Feb 14 '20 at 18:48
38

This is my approach, not sure how bad it is, please comment

In the clickable element

return (
    <th value={column} onClick={that.handleSort} data-column={column}>   {column}</th>
);

and then

handleSort(e){
    this.sortOn(e.currentTarget.getAttribute('data-column'));
}
  • This is an approach I was thinking of, it feels a little hacky but avoids creating a new component. I am not sure if getAttribute is better or worse perf-wise compared to pulling into a separate component. – kamranicus Mar 15 '17 at 05:00
  • 8
    I think it's a good solution because it is very simple. But it works only with string values, if you want an object it doesn't work. – Stephane L Mar 16 '18 at 16:41
  • For an object you would need to do `encodeURIComponent(JSON.stringify(myObj))`, then to parse it, `JSON.parse(decodeURIComponent(myObj))`. For functions I'm pretty sure this wont work without eval or new Function(), both of which should be avoided. For these reasons I don't use data-attributes to pass data in React/JS. – png Apr 20 '18 at 22:42
  • I want to add I don't use this often and only for minor things. But usually I just create a component and pass the data as props to it. Then either handle the click inside that new component or pass a onClick function to the component. Like is explained in Brandon answer – Santiago Ramirez May 04 '18 at 08:54
  • 1
    dataset can be accessed directly on this way on modern browsers (including IE11): e.currentTarget.dataset.column – gsziszi Oct 21 '18 at 15:24
  • This is a good approach, with clear and simple data, that doesn't involve smart functions trickery, and that is not affected by the so-feared performance degradation (that perhaps is significant in tables over several thousand rows). – Juan Lanus Nov 13 '19 at 22:35
32

React Hooks Solution 2022

const arr = [
  { id: 1, txt: 'One' },
  { id: 2, txt: 'Two' },
  { id: 3, txt: 'Three' },
]

const App = () => {
  const handleClick = useCallback(
    (id) => () => {
      console.log("ID: ", id)
    },
    [],
  )

  return (
    <div>
      {arr.map((item) => (
        <button onClick={handleClick(item.id)}>{item.txt}</button>
      ))}
    </div>
  )
}

You can pass a function to useCallback's return, you can then call your function normally in the render by passing params to it. Works like a charm! Just make sure you set your useCallback's dependency array appropriately.

Best Solution with React >= 16

The cleanest way I've found to call functions with multiple parameters in onClick, onChange etc. without using inline functions is to use the custom data attribute available in React 16 and above versions.

const App = () => {
  const onClick = (e) => {
    const value1 = e.currentTarget.getAttribute("data-value1")
    const value2 = e.currentTarget.getAttribute("data-value2")
    const value2 = e.currentTarget.getAttribute("data-value2")
    console.log("Values1", value1)
    console.log("Values2", value2)
    console.log("Values3", value3)
  }
  return (
    <button onClick={onClick} data-value1="a" data-value2="b" data-value3="c" />
  )
}

Above example is for a functional component but the implementation is pretty similar even in class components.

This approach doesn't yield unnecessary re-renders because you aren't using inline functions, and you avoid the hassle of binding with this.

It allows you to pass as many values as you would like to use in your function.

If you are passing values as props to your children to be used in the Child Component's onClick, you can use this approach there as well, without creating a wrapper function.

Works with array of objects as well, in cases where you want to pass the id from the object to the onClick, as shown below.

const App = () => {
  const [arrState, setArrState] = useState(arr)

  const deleteContent = (e) => {
    const id = e.currentTarget.getAttribute("data-id")
    const tempArr = [...arrState]
    const filteredArr = tempArr.filter((item) => item.id !== id)
    setArrState(filteredArr)
  }

  return (
    <div>
      {arrState.map((item) => (
        <React.Fragment key={item.id}>
          <p>{item.content}</p>
          <button onClick={deleteContent} data-id={item.id}>
            Delete
          </button>
        </React.Fragment>
      ))}
    </div>
  )
}
Tom Bombadil
  • 3,455
  • 1
  • 13
  • 24
  • 1
    I love this `data-` comment. So clean and simple – Chima - UserAndI Feb 03 '22 at 09:42
  • Actually the use of `useEffect` in your example won't help, as the callback returned by `useCallback` is still going to create new functions on every render. – Marces Engel Feb 04 '22 at 14:19
  • @MarcesEngel Depends on what you pass to the useCallback's array. – Tom Bombadil Feb 08 '22 at 14:21
  • @TomBombadil would you elaborate? Only the creation of the outer function is memoized, not its results. This means the returned function will create a new function on each execution, in this case each render of the containing component. Is there any fault in this train of thought? – Marces Engel Feb 09 '22 at 13:30
  • @MarcesEngel Yeah you are probably right. But where I work, the projects have strict ES Lint rules, so no inline functions. The first one surely works to appease ES Lint. Also, I did not really mention anything about performance improvements really. Just that this is also one way. I usually go with the second one. A little bit more work but in most cases works. You can toString an entire object and pass it in for the second. Stupid perhaps, but React.FC doesn't really give us a lot in respect to passing params to functions in JSX. – Tom Bombadil Feb 11 '22 at 06:18
  • I like that solution of yours as well. In the recent past I tend to just create an extra component for the button, pass it the function as well as the required arguments and make use of `useCallback` in there. This way you get everything. However I've also been wondering how worth memoization is at a certain level (i.e. if it doesn't reduce rerenders). Then again because of function scoping creating new functions is pretty expensive in JS, so... Guess one would have to benchmark it. – Marces Engel Feb 11 '22 at 11:44
  • One thing I wondered with the solution using the data-attribute... will the attribute not show up in the html. So it may be not so good to use it for id's or other data the user should not have. – Flummiboy Jun 17 '22 at 09:06
  • @Flummiboy Yes, it shows up in the DOM. Then again users can always get the ids from the network tab if they are malicious and knowledgable since ids come from the backend. – Tom Bombadil Jul 06 '22 at 02:59
  • It's rare really for people to dig thru the DOM for a bunch of ids. I don't really see a lot of security threats with showing ids because one can have access to ids from other tabs in the inspect tool. And there are many other ways also to get access to such things. Even decoded JWT tokens reveal user ids. Again, really depends on the use case. If the data is really to be hidden from the user then do not send it to the front-end. Always a risk involved. – Tom Bombadil Jul 06 '22 at 03:08
23

this example might be little different from yours. but i can assure you that this is the best solution you can have for this problem. i have searched for days for a solution which has no performance issue. and finally came up with this one.

class HtmlComponent extends React.Component {
  constructor() {
    super();
    this.state={
       name:'MrRehman',
    };
    this.handleClick= this.handleClick.bind(this);
  }

  handleClick(event) {
    const { param } = e.target.dataset;
    console.log(param);
    //do what you want to do with the parameter
  }

  render() {
    return (
      <div>
        <h3 data-param="value what you wanted to pass" onClick={this.handleClick}>
          {this.state.name}
        </h3>
      </div>
    );
  }
}

UPDATE

incase you want to deal with objects that are supposed to be the parameters. you can use JSON.stringify(object) to convert to it to string and add to the data set.

return (
   <div>
     <h3 data-param={JSON.stringify({name:'me'})} onClick={this.handleClick}>
        {this.state.name}
     </h3>
   </div>
);
ravibagul91
  • 20,072
  • 5
  • 36
  • 59
hannad rehman
  • 4,133
  • 3
  • 33
  • 55
  • 2
    this does not work when the data passed is an object – SlimSim Aug 19 '17 at 18:04
  • use JSON.stringify to fix the issue. @SlimSim . that should do the trick – hannad rehman Aug 21 '17 at 09:21
  • 1
    If you need to use JSON.stringify for this problem then its probably not the correct method. The process of stringification takes a lot of memory. – KFE Nov 03 '17 at 13:54
  • 1
    in most of the cases you would only pass ID as params, and get the object details based on that ID from your source object. and why and how does it take a lot of memory, i know JSON stringify is slow, but the click Fn is async and it will have no or 0 effect on dom, once constructed – hannad rehman Nov 03 '17 at 13:56
11

Simply create a function like this

  function methodName(params) {
    //the thing  you wanna do
  }

and call it in the place you need

 <Icon onClick = {() => { methodName(theParamsYouwantToPass);} }/>
Charith Jayasanka
  • 4,033
  • 31
  • 42
  • 1
    hahaha, it's crazy that this is working! You don't even need the curly braces and parenthesis. Anyone knows how React handles the anonymous function here? – Matthias Pitscher Oct 02 '21 at 23:18
9
class extends React.Component {
    onClickDiv = (column) => {
        // do stuff
    }
    render() {
        return <div onClick={() => this.onClickDiv('123')} />
    }
}
Vladimirs Matusevics
  • 1,092
  • 12
  • 21
7

I realize this is pretty late to the party, but I think a much simpler solution could satisfy many use cases:

    handleEdit(event) {
        let value = event.target.value;
    }

    ...

    <button
        value={post.id}
        onClick={this.handleEdit} >Edit</button>

I presume you could also use a data- attribute.

Simple, semantic.

jhchnc
  • 439
  • 6
  • 17
6

Making alternate attempt to answer OP's question including e.preventDefault() calls:

Rendered link (ES6)

<a href="#link" onClick={(e) => this.handleSort(e, 'myParam')}>

Component Function

handleSort = (e, param) => {
  e.preventDefault();
  console.log('Sorting by: ' + param)
}
Po Rith
  • 529
  • 4
  • 5
5

One more option not involving .bind or ES6 is to use a child component with a handler to call the parent handler with the necessary props. Here's an example (and a link to working example is below):

var HeaderRows = React.createClass({
  handleSort:  function(value) {
     console.log(value);
  },
  render: function () {
      var that = this;
      return(
          <tr>
              {this.props.defaultColumns.map(function (column) {
                  return (
                      <TableHeader value={column} onClick={that.handleSort} >
                        {column}
                      </TableHeader>
                  );
              })}
              {this.props.externalColumns.map(function (column) {
                  // Multi dimension array - 0 is column name
                  var externalColumnName = column[0];
                  return ( <th>{externalColumnName}</th>
                  );
              })}
          </tr>);
      )
  }
});

// A child component to pass the props back to the parent handler
var TableHeader = React.createClass({
  propTypes: {
    value: React.PropTypes.string,
    onClick: React.PropTypes.func
  },
  render: function () {
    return (
      <th value={this.props.value} onClick={this._handleClick}
        {this.props.children}
      </th>
    )        
  },
  _handleClick: function () {
    if (this.props.onClick) {
      this.props.onClick(this.props.value);
    }
  }
});

The basic idea is for the parent component to pass the onClick function to a child component. The child component calls the onClick function and can access any props passed to it (and the event), allowing you to use any event value or other props within the parent's onClick function.

Here's a CodePen demo showing this method in action.

Brett DeWoody
  • 59,771
  • 29
  • 135
  • 184
5

You can simply do it if you are using ES6.

export default class Container extends Component {
  state = {
    data: [
        // ...
    ]
  }

  handleItemChange = (e, data) => {
      // here the data is available 
      // ....
  }
  render () {
     return (
        <div>
        {
           this.state.data.map((item, index) => (
              <div key={index}>
                  <Input onChange={(event) => this.handItemChange(event, 
                         item)} value={item.value}/>
              </div>
           ))
        }
        </div>
     );
   }
 }
Pramesh Bajracharya
  • 2,153
  • 3
  • 28
  • 54
Louis Alonzo
  • 59
  • 1
  • 1
5

There are couple of ways to pass parameter in event handlers, some are following.

You can use an arrow function to wrap around an event handler and pass parameters:

<button onClick={() => this.handleClick(id)} />

above example is equivalent to calling .bind or you can explicitly call bind.

<button onClick={this.handleClick.bind(this, id)} />

Apart from these two approaches, you can also pass arguments to a function that is defined as a curry function.

handleClick = (id) => () => {
    console.log("Hello, your ticket number is", id)
};

<button onClick={this.handleClick(id)} />
Umair Ahmed
  • 8,337
  • 4
  • 29
  • 37
4

I wrote a wrapper component that can be reused for this purpose that builds on the accepted answers here. If all you need to do is pass a string however, then just add a data-attribute and read it from e.target.dataset (like some others have suggested). By default my wrapper will bind to any prop that is a function and starts with 'on' and automatically pass the data prop back to the caller after all the other event arguments. Although I haven't tested it for performance, it will give you the opportunity to avoid creating the class yourself, and it can be used like this:

const DataButton = withData('button')

const DataInput = withData('input');

or for Components and functions

const DataInput = withData(SomeComponent);

or if you prefer

const DataButton = withData(<button/>)

declare that Outside your container (near your imports)

Here is usage in a container:

import withData from './withData';
const DataInput = withData('input');

export default class Container extends Component {
    state = {
         data: [
             // ...
         ]
    }

    handleItemChange = (e, data) => {
        // here the data is available 
        // ....
    }

    render () {
        return (
            <div>
                {
                    this.state.data.map((item, index) => (
                        <div key={index}>
                            <DataInput data={item} onChange={this.handleItemChange} value={item.value}/>
                        </div>
                    ))
                }
            </div>
        );
    }
}

Here is the wrapper code 'withData.js:

import React, { Component } from 'react';

const defaultOptions = {
    events: undefined,
}

export default (Target, options) => {
    Target = React.isValidElement(Target) ? Target.type : Target;
    options = { ...defaultOptions, ...options }

    class WithData extends Component {
        constructor(props, context){
            super(props, context);
            this.handlers = getHandlers(options.events, this);        
        }

        render() {
            const { data, children, ...props } = this.props;
            return <Target {...props} {...this.handlers} >{children}</Target>;
        }

        static displayName = `withData(${Target.displayName || Target.name || 'Component'})`
    }

    return WithData;
}

function getHandlers(events, thisContext) {
    if(!events)
        events = Object.keys(thisContext.props).filter(prop => prop.startsWith('on') && typeof thisContext.props[prop] === 'function')
    else if (typeof events === 'string')
        events = [events];

    return events.reduce((result, eventType) => {
        result[eventType] = (...args) => thisContext.props[eventType](...args, thisContext.props.data);
        return result;
    }, {});
}
SlimSim
  • 606
  • 6
  • 15
4

Implementing show total count from an object by passing count as a parameter from main to sub components as described below.

Here is MainComponent.js

import React, { Component } from "react";

import SubComp from "./subcomponent";

class App extends Component {

  getTotalCount = (count) => {
    this.setState({
      total: this.state.total + count
    })
  };

  state = {
    total: 0
  };

  render() {
    const someData = [
      { name: "one", count: 200 },
      { name: "two", count: 100 },
      { name: "three", count: 50 }
    ];
    return (
      <div className="App">
        {someData.map((nameAndCount, i) => {
          return (
            <SubComp
              getTotal={this.getTotalCount}
              name={nameAndCount.name}
              count={nameAndCount.count}
              key={i}
            />
          );
        })}
        <h1>Total Count: {this.state.total}</h1>
      </div>
    );
  }
}

export default App;

And Here is SubComp.js

import React, { Component } from 'react';
export default class SubComp extends Component {

  calculateTotal = () =>{
    this.props.getTotal(this.props.count);
  }

  render() {
    return (
      <div>
        <p onClick={this.calculateTotal}> Name: {this.props.name} || Count: {this.props.count}</p>
      </div>
    )
  }
};

Try to implement above and you will get exact scenario that how pass parameters works in reactjs on any DOM method.

anand
  • 370
  • 2
  • 5
3

I have below 3 suggestion to this on JSX onClick Events -

  1. Actually, we don't need to use .bind() or Arrow function in our code. You can simple use in your code.

  2. You can also move onClick event from th(or ul) to tr(or li) to improve the performance. Basically you will have n number of "Event Listeners" for your n li element.

    So finally code will look like this:
    <ul onClick={this.onItemClick}>
        {this.props.items.map(item =>
               <li key={item.id} data-itemid={item.id}>
                   ...
               </li>
          )}
    </ul>
    

    // And you can access item.id in onItemClick method as shown below:

    onItemClick = (event) => {
       console.log(e.target.getAttribute("item.id"));
    }
    
  3. I agree with the approach mention above for creating separate React Component for ListItem and List. This make code looks good however if you have 1000 of li then 1000 Event Listeners will be created. Please make sure you should not have much event listener.

    import React from "react";
    import ListItem from "./ListItem";
    export default class List extends React.Component {
    
        /**
        * This List react component is generic component which take props as list of items and also provide onlick
        * callback name handleItemClick
        * @param {String} item - item object passed to caller
        */
        handleItemClick = (item) => {
            if (this.props.onItemClick) {
                this.props.onItemClick(item);
            }
        }
    
        /**
        * render method will take list of items as a props and include ListItem component
        * @returns {string} - return the list of items
        */
        render() {
            return (
                <div>
                  {this.props.items.map(item =>
                      <ListItem key={item.id} item={item} onItemClick={this.handleItemClick}/>
                  )}
                </div>
            );
        }
    
    }
    
    
    import React from "react";
    
    export default class ListItem extends React.Component {
        /**
        * This List react component is generic component which take props as item and also provide onlick
        * callback name handleItemClick
        * @param {String} item - item object passed to caller
        */
        handleItemClick = () => {
            if (this.props.item && this.props.onItemClick) {
                this.props.onItemClick(this.props.item);
            }
        }
        /**
        * render method will take item as a props and print in li
        * @returns {string} - return the list of items
        */
        render() {
            return (
                <li key={this.props.item.id} onClick={this.handleItemClick}>{this.props.item.text}</li>
            );
        }
    }
    
Tom Fuller
  • 5,291
  • 7
  • 33
  • 42
  • 1
    This does not work when the data you need to pass is an object. The attribute will only work with strings. Also reading from the dom via get attribute is probably a more expensive operation. – SlimSim Aug 19 '17 at 17:38
3

I have added code for onclick event value pass to the method in two ways . 1 . using bind method 2. using arrow(=>) method . see the methods handlesort1 and handlesort

var HeaderRows  = React.createClass({
    getInitialState : function() {
      return ({
        defaultColumns : ["col1","col2","col2","col3","col4","col5" ],
        externalColumns : ["ecol1","ecol2","ecol2","ecol3","ecol4","ecol5" ],

      })
    },
    handleSort:  function(column,that) {
       console.log(column);
       alert(""+JSON.stringify(column));
    },
    handleSort1:  function(column) {
       console.log(column);
       alert(""+JSON.stringify(column));
    },
    render: function () {
        var that = this;
        return(
        <div>
            <div>Using bind method</div>
            {this.state.defaultColumns.map(function (column) {
                return (
                    <div value={column} style={{height : '40' }}onClick={that.handleSort.bind(that,column)} >{column}</div>
                );
            })}
            <div>Using Arrow method</div>

            {this.state.defaultColumns.map(function (column) {
                return (
                    <div value={column} style={{height : 40}} onClick={() => that.handleSort1(column)} >{column}</div>

                );
            })}
            {this.state.externalColumns.map(function (column) {
                // Multi dimension array - 0 is column name
                var externalColumnName = column;
                return (<div><span>{externalColumnName}</span></div>
                );
            })}

        </div>);
    }
});
Merugu Prashanth
  • 851
  • 1
  • 6
  • 6
3

Below is the example which passes value on onClick event.

I used es6 syntax. remember in class component arrow function does not bind automatically, so explicitly binding in constructor.

class HeaderRows extends React.Component {

    constructor(props) {
        super(props);
        this.handleSort = this.handleSort.bind(this);
    }

    handleSort(value) {
        console.log(value);
    }

    render() {
        return(
            <tr>
                {this.props.defaultColumns.map( (column, index) =>
                    <th value={ column } 
                        key={ index } 
                        onClick={ () => this.handleSort(event.target.value) }>
                        { column }
                    </th>
                )}

                {this.props.externalColumns.map((column, index)  =>
                    <th value ={ column[0] }
                        key={ index }>
                        {column[0]}
                    </th>
                )}
            </tr>
         );
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Karan Singh
  • 31
  • 1
  • 4
3

I guess you will have to bind the method to the React’s class instance. It’s safer to use a constructor to bind all methods in React. In your case when you pass the parameter to the method, the first parameter is used to bind the ‘this’ context of the method, thus you cannot access the value inside the method.

3
1. You just have to use an arrow function in the Onclick event like this: 

<th value={column} onClick={() => that.handleSort(theValue)} >{column}</th>

2.Then bind this in the constructor method:
    this.handleSort = this.handleSort.bind(this);

3.And finally get the value in the function:
  handleSort(theValue){
     console.log(theValue);
}
Juan David Arce
  • 814
  • 8
  • 14
2

Using arrow function :

You must install stage-2:

npm install babel-preset-stage-2 :

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            value=0
        }
    }

    changeValue = (data) => (e) => {
        alert(data);  //10
        this.setState({ [value]: data })
    }

    render() {
        const data = 10;
        return (
            <div>
                <input type="button" onClick={this.changeValue(data)} />
            </div>
        );
    }
}
export default App; 
Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
SM Chinna
  • 341
  • 2
  • 7
2

Theres' a very easy way.

 onClick={this.toggleStart('xyz')} . 
  toggleStart= (data) => (e) =>{
     console.log('value is'+data);  
 }
2
class TableHeader extends Component {
  handleClick = (parameter,event) => {
console.log(parameter)
console.log(event)

  }

  render() {
    return (
      <button type="button" 
onClick={this.handleClick.bind(this,"dataOne")}>Send</button>
    );
  }
}
Anik Mazumder
  • 188
  • 2
  • 9
  • 4
    While this code may solve the question, [including an explanation](//s.tk/meta/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – 4b0 Mar 10 '19 at 07:13
2

Coming out of nowhere to this question, but i think .bind will do the trick. Find the sample code below.

const handleClick = (data) => {
    console.log(data)
}

<button onClick={handleClick.bind(null, { title: 'mytitle', id: '12345' })}>Login</button>
Charitha Goonewardena
  • 4,418
  • 2
  • 36
  • 38
0

There are 3 ways to handle this :-

  1. Bind the method in constructor as :-

    export class HeaderRows extends Component {
       constructor() {
           super();
           this.handleSort = this.handleSort.bind(this);
       }
    }
    
  2. Use the arrow function while creating it as :-

    handleSort = () => {
        // some text here
    }
    
  3. Third way is this :-

    <th value={column} onClick={() => that.handleSort} >{column}</th>
    
Hiren Gohel
  • 4,942
  • 6
  • 29
  • 48
Mansi Teharia
  • 1,037
  • 11
  • 11
0

You can use your code like this:

<th value={column} onClick={(e) => that.handleSort(e, column)} >{column}</th>

Here e is for event object, if you want to use event methods like preventDefault() in your handle function or want to get target value or name like e.target.name.

Til
  • 5,150
  • 13
  • 26
  • 34
0

There were a lot of performance considerations, all in the vacuum.
The issue with this handlers is that you need to curry them in order to incorporate the argument that you can't name in the props.
This means that the component needs a handler for each and every clickable element. Let's agree that for a few buttons this is not an issue, right?
The problem arises when you are handling tabular data with dozens of columns and thousands of rows. There you notice the impact of creating that many handlers.

The fact is, I only need one.
I set the handler at the table level (or UL or OL...), and when the click happens I can tell which was the clicked cell using data available since ever in the event object:

nativeEvent.target.tagName
nativeEvent.target.parentElement.tagName 
nativeEvent.target.parentElement.rowIndex
nativeEvent.target.cellIndex
nativeEvent.target.textContent

I use the tagname fields to check that the click happened in a valid element, for example ignore clicks in THs ot footers.
The rowIndex and cellIndex give the exact location of the clicked cell.
Textcontent is the text of the clicked cell.

This way I don't need to pass the cell's data to the handler, it can self-service it.
If I needed more data, data that is not to be displayed, I can use the dataset attribute, or hidden elements.
With some simple DOM navigation it's all at hand.
This has been used in HTML since ever, since PCs were much easier to bog.

Juan Lanus
  • 2,293
  • 23
  • 18
0

When working with a function as opposed to a class, it's actually fairly easy.


    const [breakfastMain, setBreakFastMain] = useState("Breakfast");

const changeBreakfastMain = (e) => {
    setBreakFastMain(e.target.value);
//sometimes "value" won't do it, like for text, etc. In that case you need to 
//write 'e.target/innerHTML'
 }

<ul  onClick={changeBreakfastMain}>
   <li>
"some text here"
   </li>
<li>
"some text here"
   </li>
</ul>
yfalik
  • 21
  • 3
0

I'd do it like this:

const HeaderRows = props => {
    const handleSort = value => () => {

    }

    return <tr>
        {props.defaultColumns.map((column, i) =>
            <th key={i} onClick={handleSort(column)}>{column}</th>)}
        {props.externalColumns.map((column, i) => {
            // Multi dimension array - 0 is column name
            const externalColumnName = column[0]
            return (<th key={i}>{externalColumnName}</th>)
        })}
    </tr>
}
francis
  • 3,852
  • 1
  • 28
  • 30
0

You can pass values from parameter to parameter

return (
  <th value={column} onClick={(column) => this.handleSort(column)}>{column}</th>
);

In this scenario I pass the column value to function.

0

Easy way

<IconButton aria-label="delete" color="error" size="small">
  <DeleteIcon onClick={() => handleFilterDelete(id)} />
</IconButton>

And add new function

function handleFilterDelete(id){
  console.log("FilterDelete",id);
}
Wongjn
  • 8,544
  • 2
  • 8
  • 24
tyler_flx
  • 11
  • 1
-1

You just need to use Arrow function to pass value.

<button onClick={() => this.props.onClickHandle("StackOverFlow")}>

Make sure to use () = >, Otherwise click method will be called without click event.

Note : Crash checks default methods

Please find below running code in codesandbox for the same.

React pass value with method

-1

Use this.handleSort. There's no keyword that in js. Thanks brother.

onClick={this.handleSort}
Showrin Barua
  • 1,737
  • 1
  • 11
  • 22
  • In 2015, `var that = this;` was a [common way around losing the `this` context](https://stackoverflow.com/q/20279484/1218980) within callback functions. – Emile Bergeron Jun 24 '21 at 00:43
-3

Use a closure, it result in a clean solution:

<th onClick={this.handleSort(column)} >{column}</th>

handleSort function will return a function that have the value column already set.

handleSort: function(value) { 
    return () => {
        console.log(value);
    }
}

The anonymous function will be called with the correct value when the user click on the th.

Example: https://stackblitz.com/edit/react-pass-parameters-example

Romel Gomez
  • 325
  • 2
  • 19
-4

Simple changed is required:

Use <th value={column} onClick={that.handleSort} >{column}</th>

instead of <th value={column} onClick={that.handleSort} >{column}</th>

-5

I think, .bind(this, arg1, arg2, ...) in React's map - is bad code, because it is slow! 10-50 of .bind(this) in single render method - very slow code.
I fix it like this:
Render method
<tbody onClick={this.handleClickRow}>
map of <tr data-id={listItem.id}>
Handler
var id = $(ev.target).closest('tr').data().id

Full code below:

class MyGrid extends React.Component {
  // In real, I get this from props setted by connect in Redux
  state = {
    list: [
      {id:1, name:'Mike'},
      {id:2, name:'Bob'},
      {id:3, name:'Fred'}
    ],
    selectedItemId: 3
  }
  
  render () {
    const { list, selectedItemId }  = this.state
    const { handleClickRow } = this

    return (
      <table>
        <tbody onClick={handleClickRow}>
          {list.map(listItem =>(
            <tr key={listItem.id} data-id={listItem.id} className={selectedItemId===listItem.id ? 'selected' : ''}>
              <td>{listItem.id}</td>
              <td>{listItem.name}</td>
            </tr>
          ))}
        </tbody>
      </table>
    )
  }
  
  handleClickRow = (ev) => {
    const target = ev.target
    // You can use what you want to find TR with ID, I use jQuery
    const dataset = $(target).closest('tr').data()
    if (!dataset || !dataset.id) return
    this.setState({selectedItemId:+dataset.id})
    alert(dataset.id) // Have fun!
  }
}

ReactDOM.render(<MyGrid/>, document.getElementById("react-dom"))
table {
  border-collapse: collapse;
}

.selected td {
  background-color: rgba(0, 0, 255, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="react-dom"></div>
MiF
  • 638
  • 7
  • 13
  • Please, write comments for what you minus – MiF Mar 01 '17 at 13:45
  • 2
    Mixing JQuery with React is not a recommended way of using React, especially if you're unit testing your code. The most idiomatic answer is to separate your list items into child components because they have actions that presumably affect application state. – kamranicus Mar 15 '17 at 05:04
  • I know, but it is just only for simplest demonstration of my solution. – MiF Mar 15 '17 at 15:17
  • 1
    In your code, jQuery does more than just demonstrate your solution. For handlers on `` itself, you can leave out `closest` and need no jQuery at all, so your solution is just like others using `data-*`. For handlers on components inside of ``, you need some DOM manipulations... that's bad as already said, but such handlers weren't asked for. – maaartinus Jul 08 '18 at 16:15
  • You right. Now I think, that the best way is to create a nested component and return it from map function. In this way, you have no work with DOM and create a nested component with bindings will be done once. – MiF Jul 15 '18 at 15:35